Algorithm 如何检查循环单链表是否为pallindrome?

Algorithm 如何检查循环单链表是否为pallindrome?,algorithm,data-structures,linked-list,Algorithm,Data Structures,Linked List,问题:我有一个单一的链表(即只有指向下一个节点的指针的列表)。此外,这是一个循环链表(在本例中,最后一个节点有一个指向第一个节点的指针)。列表中的每个节点都包含一个字符 此类列表的示例可以是:a->b->c->b->a 现在,我如何验证此列表是否为苍白罗马 我想到了以下解决方案: 从榜首开始。找到列表的长度,然后找到中间节点。现在再次从列表的开头开始,并继续将元素推入堆栈直到中间。现在从中间和弹出元素遍历列表。如果弹出元素的值等于当前节点的值。如果不是,则返回false。否则,继续,直到堆栈为空

问题:我有一个单一的链表(即只有指向下一个节点的指针的列表)。此外,这是一个循环链表(在本例中,最后一个节点有一个指向第一个节点的指针)。列表中的每个节点都包含一个字符

此类列表的示例可以是:a->b->c->b->a

现在,我如何验证此列表是否为苍白罗马

我想到了以下解决方案:

  • 从榜首开始。找到列表的长度,然后找到中间节点。现在再次从列表的开头开始,并继续将元素推入堆栈直到中间。现在从中间和弹出元素遍历列表。如果弹出元素的值等于当前节点的值。如果不是,则返回false。否则,继续,直到堆栈为空并且我们已经验证了所有字符。缺点:使用额外的堆栈空间:(

  • 从列表的开头开始。找到列表的长度,然后找到中间节点。现在反转列表的第二部分。然后使用两个指针(一个指向开始,另一个指向中间+1'元素),检查值是否相同。如果不相同,则返回false。否则继续,直到我们再次到达开始节点。缺点:更改原始数据结构

  • 有没有一种更优雅的方法来解决这个问题(希望不会使用O(n)额外的空间或更改原始列表)?我对算法感兴趣,而不是任何具体的实现

    感谢

    这是pseudo Haskell(我现在记不清确切的语法)中的内容,我已经为非循环的情况编写了一篇文章——为了解决这个问题,只需将与[]匹配的子句替换为您用来标识已完成循环的任何条件

    p(xs) = q(xs, Just(xs)) != Nothing
    
    
    q([], maybeYs) = maybeYs
    
    q(x : xs, Nothing) = Nothing
    
    q(x : xs, maybeYs) =
        let maybeZs = q(xs, maybeYs) in
        case maybeZs of
            Nothing        -> Nothing
            Just (x :: zs) -> Just(zs)
            otherwise      -> Nothing
    

    因为您处理的是一个单链表,所以必须使用一点额外的空间或更多的额外时间

    您的第一种方法听起来很合理,但您可以在一次运行中确定列表的长度和回文数

    我们修改了所谓的Floyd循环查找算法:

    • 两个指针“慢”和“快”,都从列表头开始;慢指针每次迭代前进一个列表元素,快指针前进两个元素

    • 在每一步中,慢速指针将当前元素推送到堆栈上
    如果快速指针到达列表的末尾,则慢速指针指向列表的中间,因此现在:

    • 慢速指针前进到列表的末尾,在每个步骤中:
    • 它从堆栈中弹出一个元素,并将其与当前列表元素进行比较(如果它们不相等,则返回
      false
    • 如果慢速指针到达列表的末尾,则为回文

    对于元素数为奇数的列表,需要做一些额外的工作。

    因为您知道链表确实有一个循环,并且您只需要查找从开头开始的回文,所以您可以自己简化这一过程

    A -> B -> C -> B -> A
    
    在本例中,从head处的指针(称为H)和head.Left()处的指针(称为T)开始

    现在继续将头部指针H向右移动,尾部指针T向左移动

    在浏览列表时,请验证这些元素的值是否相等(即回文)

    但是,您的停止条件需要更多。有两种情况:

    • 两个指针在同一个元素上结束(即奇数个元素)
    • H指针指向T右边的元素
    因此,如果H==T或H==(T.Right()),则停止

    使用这种方法(或类似方法),您只需访问每个元素一次


    如果您不知道链表是否是循环的,请使用其他解决方案中的龟兔方法。

    只需粘贴我的实现,这样我们就可以相互比较,全文如下:

    /**
    *给定一个循环单链表和起始指针,检查它是否为回文
    *使用慢/快指针+堆栈是一种优雅的方式
    *提示:如果存在循环链表,请考虑使用慢速/快速指针
    */
    #包括
    #包括
    使用名称空间std;
    结构体类型
    {
    字符c;
    节点*下一步;
    节点(字符c){this->c=c;}
    节点*链节点(字符c)
    {
    节点*p=新节点(c);
    p->next=NULL;
    这个->下一个=p;
    返回p;
    }
    };
    布尔isAlindrome(节点*pStart)
    {
    节点*pSlow=pStart;
    节点*pFast=pStart;
    堆栈s;
    布尔贝文=假;
    while(true)
    {
    //错误1:首先检查快速指针
    pFast=pFast->next;
    if(pFast==pStart)
    {
    贝文=假;
    打破
    }
    其他的
    {
    pFast=pFast->next;
    if(pFast==pStart)
    {
    贝文=真;
    打破
    }
    }
    pSlow=pSlow->next;
    s、 推(pSlow);
    }
    如果(s.empty())返回true;//BUG2:a,a->b->a
    如果(bEven)pSlow=pSlow->next;//BUG3:a->b->c->b->a,a->b->c->d->c->b->a:跳过中心指针
    而(!s.empty())
    {
    //弹出堆栈和高级链表
    Node*topNode=s.top();
    s、 pop();
    pSlow=pSlow->next;
    //检查
    如果(topNode->c!=pSlow->c)
    {
    返回false;
    }
    其他的
    {
    如果(s.empty())返回true;
    }
    }
    返回false;
    }
    
    我想我们不需要额外的空间来做这个。这可以用O(n)复杂度来完成

    修改Philip的解决方案:

    我们修改了所谓的Floyd循环查找算法:

    两个指针“慢”和“快”,都从列表头开始;慢指针每次迭代前进一个列表元素,快指针前进两个元素
    在每一步中,慢速指针将当前元素推送到堆栈上 如果快速指针到达列表的末尾,则慢速指针指向列表的中间,因此现在:

    在链表的开头有另一个指针
    /**
     * Given a circular single linked list and the start pointer, check if it is a palindrome
     * use a slow/fast pointer + stack is an elegant way
     * tip: wheneve there is a circular linked list, think about using slow/fast pointer
     */
    
    #include <iostream>
    #include <stack>
    using namespace std;
    
    struct Node
    {
        char c;
        Node* next;
    
        Node(char c) {this->c = c;}
        Node* chainNode(char c)
        {
            Node* p = new Node(c);
            p->next = NULL;
            this->next = p;
            return p;
        }
    };
    
    bool isPalindrome(Node* pStart)
    {
        Node* pSlow = pStart;
        Node* pFast = pStart;
    
        stack<Node*> s;
        bool bEven = false;
        while(true)
        {
            // BUG1: check fast pointer first
            pFast = pFast->next;
            if(pFast == pStart)
            {
                bEven = false;
                break;
            }
            else
            {
                pFast = pFast->next;
                if(pFast == pStart)
                {
                    bEven = true;
                    break;
                }
            }
    
            pSlow = pSlow->next;
            s.push(pSlow);
    
        }
        if(s.empty()) return true; // BUG2: a, a->b->a
        if(bEven) pSlow = pSlow->next; // BUG3: a->b->c->b->a, a->b->c->d->c->b->a: jump over the center pointer
    
        while(!s.empty())
        {
            // pop stack and advance linked list
            Node* topNode = s.top();
            s.pop();
            pSlow = pSlow->next;
    
            // check
            if(topNode->c != pSlow->c)
            {
                return false;
            }
            else
            {
                if(s.empty()) return true;
            }
        }
    
        return false;
    }