您的位置:首页 > 其它

关于Linked List Cycle II (leetcode 142) 的几点思考

2015-07-25 15:09 357 查看
题目如是说:

Given a linked list, return the node where the cycle begins. If there is no cycle, return
null


Can you solve it without using extra space?
给定一个链表(头指针),能否只用常数的空间判断一个链表是不是有环,如果有环,返回环的起始位置。

单个节点的定义也已经给出:

对于判断链表是否有环,方法并不难:使用一块一慢两个指针从起点开始行走。快指针每次走2步,慢指针每次走1步。如果链表中有环,二者必然会在环中某点相遇。

1 设链表长度为n(链表中非空next指针的个数,下面所说的长度均为非空next指针的个数),链表head到环的起点长度为k,环的长度为r显然r=n-k。

2 假设两相遇时,慢指针移动的长度为l,则快指针移动长度为2l,而快指针移动的长度还等于l在加上在环上绕的圈,以下图为例:



k=1, n=4,在慢指针行走了3步以后,两者第一次相遇,快指针刚好比慢指针多走了一个环的长度,于是我们有如下第3步:

3 我们从链表头、与相遇点分别设一个指针,每次各走一步,慢指针走了k步到达环的起点,与快指针在环起点相遇。

遵循这样的思路,我们容易得到题目的解决方案

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head==NULL || head->next==NULL)
            return NULL;
        ListNode * p=head;  //慢指针
        ListNode * q=head;  //快指针
        while(q!=NULL && q->next!=NULL)
        {
            p=p->next;
            q=q->next;
            q=q->next;
            if(p==q)
              break;
        }
        if(q==NULL || q->next==NULL)
          return NULL;
        p=head;
        while(p!=q)
        {
            p=p->next;
            q=q->next;
        }
        return p;
    }
};
然而事实果真如此吗?如果链表是这个样子的:



当快指针比慢指针多走了一个环长的时候,慢指针根本没有进入环!此时慢指针到达4,快指针则是在7的位置。也即,快指针比慢指针多走了若干个环长距离是两个指针相遇的必要而非充分条件!即2l=l+(n-k)*num(num>=1是两指针相遇的必要条件)。事实上,对于本例而言,两者第一次相遇时,l=6,即两者相遇在节点7。

有意思的是,这并不影响我们前述代码的正确性,为什么呢?上例中,快指针比慢指针多走了一个环长时,两者并未相遇,但是只要链表中存在环结构,两者总会相遇,只不过两者第一次相遇时,num=N(N可能是一个比较大的正整数),此时快指针比慢指针多走了N个环的路程,而从此时开始,慢指针每走一圈,两者就会重新相遇一次。

从两者第一次相遇开始,假设快指针走的距离2l=l+(n-k)*N,得l=(n-k)*N,此时指针从进入环以后,又走了距离(n-k)*N-k,指针在前进k步以后,入环后行走的距离为(n-k)*N-k+k(n-k)*N,刚好达到环的起点;而另一方面,我们从链表头设置的指针走了k步也刚好与之在环的起点相遇,于是我们还是以这种方法找到了环的起点!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: