给定一个链表,判断它是否有环。
给出 -21->10->4->5, tail connects to node index 1,返回 true
不要使用额外的空间!!
思路:如果一个链表有环, 那么它肯定只有一个环.(一个相交结点)
算法:设两个指针p, q, 初始化指向头.p以步长2的速度向前跑, q的步长是1.这样, 如果链表不存在环, p和q肯定不会相遇.如果存在环, p和q一定会相遇.(就像两个速度不同的汽车在一个环上跑绝对会相遇).复杂度O(n)
代码如下:
/**
* Definition of ListNode
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/**
* @param head: The first node of linked list.
* @return: True if it has a cycle, or false
*/
bool hasCycle(ListNode *head) {
// write your code here
ListNode *p = NULL,*q = NULL;
for(p = head,q = head;q;q = q->next,p = p->next){
q = q->next;
if(!q)break;
if(p == q)return true;
}
return false;
}
};
问题变形:
找出链表中环的入口结点
还是使用俩指针p和q, q扫描的步长为1, p扫描的步长为2.它们的相遇点为图中meet处(在环上).
假设头指针head到入口点entry之间的距离是K.则当q入环的时候, p已经领先了q为: d = K%n(n为环的周长).
我们设meet处相对entry的距离(沿行进方向)为x, 则有
(n-d)+x = 2x (p行进的路程是q的两倍)
解得x = n-d
那么当p和q在meet处相遇的时候, 从head处再发出一个步长为1的指针r, 可以知道, r和q会在entry处相遇!
算法就是:
初始化三个指针p, q, r全部指向head. 然后p以2的速度行进, q以1的速度行进.当p和q相遇的时候, 发出r指针并以1的速度行进, 当q和r相遇返回这个结点.复杂度O(n)
node_t *find_entry(node_t *head)
{
node_t *p, *q, *r;
for (p = q = head; p; p = p->next, q = q->next){
p = p->next;
if (!p) break;
if (p == q) break;
}
if (!p) return 0; //no ring in list
for (r = head, q = q->next; q != r; r = r->next, q = q->next);
return r;
}