快慢指针判断链表是否有环
对于这个问题我们可以采用“快慢指针”的方法。就是有两个指针fast和slow,开始的时候两个指针都指向链表头head,然后在每一步操作中slow向前走一步即:slow = slow->next,而fast每一步向前两步即:fast = fast->next->next。由于fast要比slow移动的快,如果有环,fast一定会先进入环,而slow后进入环。当两个指针都进入环之后,经过一定步的操作之后二者一定能够在环上相遇,并且此时slow还没有绕环一圈,也就是说一定是在slow走完第一圈之前相遇。证明可以看下图:
从上面的分析知道,当fast和slow相遇时,slow还没有走完链表,假设fast已经在环内循环了n(1<= n)圈。假设slow走了s步,则fast走了2s步,又由于fast走过的步数 = s + nr(s + 在环上多走的n圈),则有下面的等式:
2s = s + n * r ; (1)
=> s = n*r (2)
如果假设整个链表的长度是L,入口和相遇点的距离是x(如上图所示),起点到入口点的距离是a(如上图所示),则有:
a + x = s = n * r; (3)
由(2)推出
a + x = (n - 1) * r + r = (n - 1) * r + (L - a) (4) 由环的长度 = 链表总长度 - 起点到入口点的距离求出
a = (n - 1) * r + (L -a -x) (5)
集合式子(5)以及上图我们可以看出,从链表起点head开始到入口点的距离a,与从slow和fast的相遇点(如图)到入口点的距离相等。
所以我们设置一个快指针一个慢指针,当慢指针走了一个a+x的路程,快指针要走2(a+x)的路程,从慢指针处还要再走一个a+x路程,又从上式可以知道a=(n-1)*r+(L-a-x),所以我们快指针相当于又走了环内相遇点到入口处+x的路程,所以快指针与慢指针相遇。
**所以思路是设置快慢指针,两指针从头结点出发,当两指针相遇,即为有环。代码如下:
**
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode *p = head;
ListNode *q = head;
while(p && p->next){
p = p->next->next;
q = q->next;
if(q==p){
return true;
}
}
return false;
}
};