这是力扣上的一道题,题目是这样的:
存在环的链表和正常的链表不同在于存在环的链表不能遍历,因为没有尾部的存在。
在开始做题的时候,博主想到的方法是每经过一个节点,就将这个节点断开,判断下一个节点所指向的节点是不是指向NULL,如果存在环的话,开始成环的节点就会指向NULL。如下图所示:
但是博主忽略了一点,就是如果链表中没有环的话,尾部也会存在NULL,这样子在倒数第二个节点也会出现上图的情况。
如上图,在节点0的位置,0所指向的节点也指向NULL,但是链表中并没有环。这就陷入了死胡同,说明这种解法是存在问题的。
之后我就想到了链表中比较常用的双指针法,也叫快慢指针。
快慢指针解题
将带环链表画的更形象一点,如下图:
在起始位置有两个指针,快指针一次走两步,慢指针一次走一步,也就是说,快指针的速度是慢指针的2倍。当快指针走到环的入口A点时,慢指针只走了前面路程的一半。
当慢指针走到环的入口时,由于不知道环的长度,快指针已经绕着环走了N圈(N>=0),但是快指针和慢指针之间必然相差距离X。
慢指针走一步,快指针走两步,相差距离变为X-1
慢指针走两步,快指针走四步,相差距离变为X-2
慢指针走三步,快指针走六步,相差距离变为X-3
…
以此类推,快指针一定会和慢指针相遇。
这就是用快慢指针的方法判断环状链表。代码如下:
bool hasCycle(struct ListNode *head) {
//可以用快慢指针来解决
if(head == NULL)
return false;
struct ListNode* slow = head;
struct ListNode* fast = head;
//因为fast指针一次走两步,也要保证fast->next不为空
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(fast == slow)
return true;
}
return false;
}
这里再解释一下为什么fast不能一次眺三步、四步…n步:
如果fast一次眺三步,快指针和慢指针的距离每次就会变为X-2、X-4、X-6…,这样如果快慢指针之间的距离是奇数就有可能错过,而且不可能再追上。
fast眺四步,之间的距离变化就是3的倍数
以此类推,所以眺两步最合适。
可以把两步的快慢指针看作一对男女,如果始终保持这样,无论多元的距离,都一定会再相遇。但是如果在前面的人走快哪怕一步,可能今生都会错过。