AcWing 34 链表中环的入口结点

题目描述:

给定一个链表,若其中包含环,则输出环的入口节点。

若其中不包含环,则输出null

样例

QQ截图20181202023846.png

给定如上所示的链表:
[1, 2, 3, 4, 5, 6]
2
注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。

则输出环的入口节点3.

分析:

很有意思的题目。难点在于我们无法像遍历图结点那样设置标志数组,第一次访问到已经访问过的点就是环的起点。对于链表,我们没法标识它的某个结点。

本题同样采用双指针,想象一下在操场上绕着环形跑道跑步,只要两个人不停的以不同的速度奔跑,跑的快的总会从后面追上跑得慢的。现在假设有两个指针p和q,p每次前进一步,q每次前进两步,那么某时刻,只要有环,他们必会相遇在某结点。反之,没环的链表,他们一定能遍历到链表的结尾。

QQ图片20180531162503.png

盗个图来解释一下。假设上图就是那个带环的链表,a是首节点,b是环的起点。q以p两倍的速度奔跑,某时刻,他们第一次相遇于c点。我们知道,p结点到达c点走了x+y的路(这里的y可能大于环长,不过不影响结果),q到达c点走了2(x+y)的路。当p第一次走到b点时,它走了x长度的路,此时q走了2x的路,其中在环上走了x长度的路,还要走2y才能到达c点,又b到c相距y,也就是这时q还要走y的路才能到达b点,可以推出x+y是环长度的倍数。第一次相遇时q在离b点相距y的位置,再走x距离就可以回到b点。所以第一次相遇后,让p回到a点,p和q从新以相同的速度再次奔跑。p再次到达b点时,恰好q也到达了b点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *entryNodeOfLoop(ListNode *head) {
        ListNode *p = head,*q = head;
        if(!p || !p->next)  return NULL;
        while(p && q){
            p = p-> next;
            q = q->next->next;
            if(p == q){
                p = head;
                while(p != q){
                    p = p->next;
                    q = q->next;
                }
                return p;
            }
        }
        return NULL;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_30277239/article/details/88238896