Algorithm 如何检查循环单链表是否为pallindrome?
问题:我有一个单一的链表(即只有指向下一个节点的指针的列表)。此外,这是一个循环链表(在本例中,最后一个节点有一个指向第一个节点的指针)。列表中的每个节点都包含一个字符 此类列表的示例可以是:a->b->c->b->a 现在,我如何验证此列表是否为苍白罗马 我想到了以下解决方案:Algorithm 如何检查循环单链表是否为pallindrome?,algorithm,data-structures,linked-list,Algorithm,Data Structures,Linked List,问题:我有一个单一的链表(即只有指向下一个节点的指针的列表)。此外,这是一个循环链表(在本例中,最后一个节点有一个指向第一个节点的指针)。列表中的每个节点都包含一个字符 此类列表的示例可以是:a->b->c->b->a 现在,我如何验证此列表是否为苍白罗马 我想到了以下解决方案: 从榜首开始。找到列表的长度,然后找到中间节点。现在再次从列表的开头开始,并继续将元素推入堆栈直到中间。现在从中间和弹出元素遍历列表。如果弹出元素的值等于当前节点的值。如果不是,则返回false。否则,继续,直到堆栈为空
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右边的元素
如果您不知道链表是否是循环的,请使用其他解决方案中的龟兔方法。只需粘贴我的实现,这样我们就可以相互比较,全文如下:
/**
*给定一个循环单链表和起始指针,检查它是否为回文
*使用慢/快指针+堆栈是一种优雅的方式
*提示:如果存在循环链表,请考虑使用慢速/快速指针
*/
#包括
#包括
使用名称空间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;
}