剑指OFFER 21.链表中环的入口结点


链表中环的入口结点

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

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

#### 样例

QQ截图20181202023846.png


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

则输出环的入口节点3.

class Solution {
public:
    ListNode *entryNodeOfLoop(ListNode *head) 
    {
    	 ListNode *h1=head,*h2=head;
    	 if(!head ||!head->next)
    	 	return NULL;
    	 while(h1 && h2)
    	 {
    	 	h1=h1->next;
    	 	h2=h2->next;
    	 	if(h2)
    	 		h2=h2->next;
    	 	else return NULL;
    	 	if(h1==h2)
    	 	{
    	 		h1=head;
    	 		while(h1!=h2)
    	 		{
    	 			h1=h1->next;
    	 			h2=h2->next;
    	 		}
    	 		return h1;
    	 	}
    	 }
    	 return NULL;   
    }
};
算法
(链表,快慢指针扫描) O(n)O(n)
本题的做法比较巧妙。
用两个指针 first,secondfirst,second 分别从起点开始走,firstfirst 每次走一步,secondsecond 每次走两步。
如果过程中 secondsecond 走到null,则说明不存在环。否则当 firstfirst 和 secondsecond 相遇后,让 firstfirst 返回起点,secondsecond 待在原地不动,然后两个指针每次分别走一步,当相遇时,相遇点就是环的入口。



证明:如上图所示,aa 是起点,bb 是环的入口,cc 是两个指针的第一次相遇点,abab 之间的距离是 xx,bcbc 之间的距离是 yy。
则当 firstfirst 走到 bb 时,由于 secondsecond 比 firstfirst 多走一倍的路,所以 secondsecond 已经从 bb 开始在环上走了 xx 步,可能多余1圈,距离 bb 还差 yy 步(这是因为第一次相遇点在 bb 之后 yy 步,我们让 firstfirst 退回 bb 点,则 secondsecond 会退 2y2y 步,也就是距离 bb 点还差 yy 步);所以 secondsecond 从 bb 点走 x+yx+y 步即可回到 bb 点,所以 secondsecond 从 cc 点开始走,走 xx 步即可恰好走到 bb 点,同时让 firstfirst 从头开始走,走 xx 步也恰好可以走到 bb 点。所以第二次相遇点就是 bb 点。

另外感谢@watay147提供的另一种思路,可以用公式来说明:a,b,c,x,ya,b,c,x,y 的含义同上,我们用 zz 表示从 cc 点顺时针走到 bb 的距离。则第一次相遇时 secondsecond 所走的距离是 x+(y+z)∗n+yx+(y+z)∗n+y, nn 表示圈数,同时 secondsecond 走过的距离是 firstfirst 的两倍,也就是 2(x+y)2(x+y),所以我们有 x+(y+z)∗n+y=2(x+y)x+(y+z)∗n+y=2(x+y),所以 x=(n−1)×(y+z)+zx=(n−1)×(y+z)+z。那么我们让 secondsecond 从 cc 点开始走,走 xx 步,会恰好走到 bb 点;让 firstfirst 从 aa 点开始走,走 xx 步,也会走到 bb 点。

时间复杂度
firstfirst 总共走了 2x+y2x+y 步,secondsecond 总共走了 2x+2y+x2x+2y+x 步,所以两个指针总共走了 5x+3y5x+3y 步。由于当第一次 firstfirst 走到 bb 点时,secondsecond 最多追一圈即可追上 firstfirst,所以 yy 小于环的长度,所以 x+yx+y 小于等于链表总长度。所以总时间复杂度是 O(n)O(n)。

C++ 代码
/**

 * 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) {
       if (!head || !head->next) return 0;
       ListNode *first = head, *second = head;

       while (first && second)
       {
           first = first->next;
           second = second->next;
           if (second) second = second->next;
           else return 0;
       
           if (first == second)
           {
               first = head;
               while (first != second)
               {
                   first = first->next;
                   second = second->next;
               }
               return first;
           }
       }
       
       return 0;

   }
   };


文章作者: LHL
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 LHL !
评论
  目录