有没有人知道一种算法,用于查找链表是否只使用两个变量来遍历列表。假设您有一个链接的对象列表,它与哪种类型的对象无关。我在一个变量中有一个指向链表头部的指针,我只有一个其他变量来遍历列表。
所以我的计划是比较指针值以查看是否有任何指针相同。该列表的大小有限,但可能很大。我可以将两个变量都设置为头部,然后用另一个变量遍历列表,总是检查它是否等于另一个变量,但是,如果我确实打了一个循环,我将永远不会离开它。我认为它与遍历列表和比较指针值的不同速率有关。有什么想法吗?
答案 0 :(得分:46)
我建议使用Floyd's Cycle-Finding Algorithm
又名 Tortoise and the Hare Algorithm
。它具有O(n)复杂度,我认为它符合您的要求。
示例代码:
function boolean hasLoop(Node startNode){
Node slowNode = Node fastNode1 = Node fastNode2 = startNode;
while (slowNode && fastNode1 = fastNode2.next() && fastNode2 = fastNode1.next()){
if (slowNode == fastNode1 || slowNode == fastNode2) return true;
slowNode = slowNode.next();
}
return false;
}
有关维基百科的更多信息:Floyd's cycle-finding algorithm。
答案 1 :(得分:17)
您可以使用Turtle and Rabbit算法。
维基百科也有一个解释,他们称之为“Floyd's cycle-finding algorithm”或“Tortoise and hare”
答案 2 :(得分:9)
绝对。一个解决方案确实可以用两个指针遍历列表,一个以两倍的速率行进。
从'slow'和'fast'指针开始,指向列表中的任何位置。运行遍历循环。如果任何时候“快速”指针与慢速指针一致,那么你就有了一个循环链表。
int *head = list.GetHead();
if (head != null) {
int *fastPtr = head;
int *slowPtr = head;
bool isCircular = true;
do
{
if (fastPtr->Next == null || fastPtr->Next->Next == null) //List end found
{
isCircular = false;
break;
}
fastPtr = fastPtr->Next->Next;
slowPtr = slowPtr->Next;
} while (fastPtr != slowPtr);
//Do whatever you want with the 'isCircular' flag here
}
答案 3 :(得分:3)
我试图自己解决这个问题,并找到了一种不同的(效率较低但仍然是最优的)解决方案。
这个想法是基于在线性时间内反转单链表。这可以通过在迭代列表的每一步进行两次交换来完成。如果q是前一个元素(最初为空)并且p是当前,则交换(q,p->下一个)交换(p,q)将反转链接并同时推进两个指针。可以使用XOR完成交换,以防止必须使用第三个内存位置。
如果列表有一个循环,那么在迭代期间的某一点,您将到达指针已被更改的节点。您无法知道哪个节点,但通过继续迭代,交换一些元素两次,您再次到达列表的头部。
通过反转列表两次,列表在结果中保持不变,您可以根据是否到达列表的原始头来判断它是否有一个周期。
答案 4 :(得分:2)
int isListCircular(ListNode* head){
if(head==NULL)
return 0;
ListNode *fast=head, *slow=head;
while(fast && fast->next){
if(fast->next->next==slow)
return 1;
fast=fast->next->next;
slow=slow->next;
}
return 0;
}
答案 5 :(得分:1)
boolean findCircular(Node *head)
{
Node *slower, * faster;
slower = head;
faster = head->next;
while(true) {
if ( !faster || !faster->next)
return false;
else if (faster == slower || faster->next == slower)
return true;
else
faster = faster->next->next;
}
}
答案 6 :(得分:0)
将此问题转移到下一步将识别循环(即,不仅仅是循环存在,而是确切地在列表中的位置)。 Tortoise和Hare算法可以用于相同的,但是,我们需要始终跟踪列表的头部。可以找到此算法的图示here。