木槿花篱

嘻嘻,欢迎~

面试题 02.08. 环路检测 快慢指针

给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。
有环链表的定义:在链表中某个节点的next元素指向在它前面出现过的节点,则表明该链表存在环路。

 

示例 1: 

输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

 

方法:快慢指针

概念:快慢指针是指移动的步长,即每次向前移动速度的快慢。(例如,让快指针每次沿链表向前移动2,慢指针每次向前移动1)

 

1. 判断存不存在环路:当快指针到达NULL的时候,说明不存在环路。遍历就可以。

2. 找到环入口:

 

假设从头结点开始,有a个结点在环外,环中有r个结点。快指针和慢指针都从头指针出发,快指针每次走 2 步,慢指针每次走 1 步,在经过一段时间以后,在环中相遇,距离环入口的距离是 X。

此时,慢指针走过的路程为 a+X;

如果 a 较大的话,快指针可能现在已经在环中走过几圈了,假设快指针从入环开始已经走过 k(k>=1) 个整圈,则快指针走过的路程为 a+X+k*r;

快指针走过的路程是慢指针的 2 倍,所以 2*(a+X) = a+X+k*r,即 a = k*r - X = (k-1)*r + r - X;

观察一下,r-X 正好是相遇结点到环入口的距离 ,也就是说,如果现在让慢指针从头结点出发,快指针从相遇结点出发,每次步长均为 1,它们会在环入口相遇。

代码:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode *detectCycle(ListNode *head) {
12         ListNode* pro;//快指针
13         ListNode* last;//慢指针
14         pro = head;
15         last = head;
16         if(!head) return head;
17         do{
18             if(!pro->next) return pro->next;
19             pro = pro->next;
20             last = last->next;
21             if(!pro->next) return pro->next;
22             pro = pro->next;
23         }while(pro != last);
24 
25         last = head;
26         while(pro != last){
27             pro = pro->next;
28             last = last->next;
29         }
30 
31         return last;
32     }
33 };

快慢指针的其他应用:

1. 在有序链表中寻找中位数。原理:快指针移动速度是慢指针移动速度的两倍。所以快指针到达链表尾时,慢指针到达中点。(需要判断链表节点奇偶数)如果快指针移动x步到达表尾则为奇数。若为倒数第二个节点则为偶数(可根据规则返回上中位或下中位或上下一半);

2. 两个单向链表判断他们是否相交

思路:首先用快慢指针判断是否有环

1)如果都没有环,如果两个单链表有公共结点,则两个单链表从某个结点开始,他们的next指向同一结点。(由于是单链表结点,每一结点只有一个next,所以从第一个公共结点开始,他们所有结点都是重合的)。既若两个单链表的末尾结点相同则相交。寻找第一个相交结点的方法:

a.在其中一个单链表上顺序遍历每个结点,每遍历一个结点,在另外一个链表上顺序遍历每个结点。

b.首先两个链表各遍历一次求表长l1,l2,两链表差为l.然后现在长的链表上遍历L,然后同步遍历,第一个相同的结点就是公共结点。时间复杂度(O(m+n))

2)如果一个存在环,另外一个不存在环则不可能相交。

3)如果利用快慢指针发现两单链表都存在环,则判断一个链表上快慢指针相遇的那个结点是否在另一个链表上。如果在则相交,否在不相交。如果相交,两个链表的入口点可能不是环上同一结点,利用上边的方法分别找出各自的入口点,可以定义任意一个入口点为相交的第一个结点。

 

posted @ 2020-11-04 09:50  木槿花篱  阅读(107)  评论(0编辑  收藏  举报