问题:html
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.node
Note: Do not modify the linked list.算法
Follow up:
Can you solve it without using extra space?spa
解决:.net
① 使用快慢指针,记录两个指针相遇的位置,当两个指针相遇了后,让其一指针从链表头开始向后走,另外一个从相遇的点开始,此时再相遇的位置就是链表中环的起始位置。指针
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution { //2ms
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {//有环
break;
}
}
if (fast == null || fast.next == null) {//不存在环
return null;
}
slow = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return fast;
}
}code
② 进化版htm
public class Solution { //1ms
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (slow != null && fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast && slow != null) {
ListNode slow2 = head;
while (slow != slow2) {
slow = slow.next;
slow2 = slow2.next;
}
return slow;
}
}
return null;
}
}blog
【总结】链表找环方法证实 http://www.cnblogs.com/wuyuegb2312/p/3183214.html同步
一: 判断两个链表是否相交,假设两个链表不带环。https://my.oschina.net/liyurong/blog/994540
解决方法:用指针p一、p2分别指向两个链表头,不断后移;最后到达各自表尾时,若p1==p2,那么两个链表必相交。
二: 若是链表可能有环,上面的方法怎么调整?
分状况讨论:
若是两个链表都没有环,那么同原算法;
若是两个链表一个有环,一个没环,那么必然不相交。
(*)若是两个链表都有环,判断一个链表环上的任一点是否在另外一个链表上,若是是,则必相交,反之不相交。这时,须要找到另外一个链表完整的环都包括了哪些结点,才能进行判断。(*)
能够看出,解答这个问题重点在于判断是否有环。
三: 若是必需要求出两个链表相交的第一个节点呢?
(*) 思路:若是两个尾结点是同样的,说明它们有重合;不然两个链表没有公共的结点。
在上面的思路中,顺序遍历两个链表到尾结点的时候,咱们不能保证在两个链表上同时到达尾结点。这是由于两个链表不必定长度同样。但若是假设一个链表比另外一个长L个结点,咱们先在长的链表上遍历L个结点,以后再同步遍历,这个时候咱们就能保证同时到达最后一个结点了。因为两个链表从第一个公共结点开始到链表的尾结点,这一部分是重合的。所以,它们确定也是同时到达第一公共结点的。因而在遍历中,第一个相同的结点就是第一个公共的结点。
在这个思路中,咱们先要分别遍历两个链表获得它们的长度,并求出两个长度之差。在长的链表上先遍历若干次以后,再同步遍历两个链表,直到找到相同的结点,或者一直到链表结束。PS:没有处理一种特殊状况:就是一个是循环链表,而另外一个也是,只是头结点所在位置不同。 (*)
对于特殊状况,相交的第一个结点能够是第一个链表的表头,也能够是第二个链表的表头。由于从表头开始两者就开始相交了。对于这种状况,若是判断出两者都是循环链表,就能够直接返回其中之一的头指针。
四: 求链表倒数第k个结点
(*)设置两个指针p1,p2,首先p1和p2都指向head,而后p2向前走k步,这样p1和p2之间就间隔k个节点,最后p1和p2同时向前移动,直至p2走到链表末尾。(*)
【总结】如今来看,遗留问题是:
1.如何判断链表有环?
2.如何找到链表环的入口?
简单来讲就是:
1. 对于问题1,指针p一、p2指向表头,每次循环p1指向后继,p2指向后继的后继;循环的结束条件是,p2后继为空(无环)或p1==p2(有环)。
下面用易于理解的方式证实,这个解法中若是有环,p1和p2必同时在停留在某个节点。
如左图,在任意时刻,p1和p2都在环上。因为p1每次向前1步,p2每次向前两步,用相对运动的观点来看,把p1看做静止,那么p2每次相对p1向前1步,两者在顺时针方向上的距离每通过一个时刻就减小1,直到变为0,也即两者刚好相遇。这样就证实了在离散状况下,对于有环链表,两者也是必然在某一时刻相遇在某个节点上的。
2. 对于问题2寻找环的入口问题。
设:链表头是X,环的第一个节点是Y,slow和fast第一次的交点是Z。各段的长度分别是a,b,c,如图所示。环的长度是L。slow和fast的速度分别是qs,qf。
第一次相遇时slow走过的距离:a+b,fast走过的距离:a+b+c+b。
由于fast的速度是slow的两倍,因此fast走的距离是slow的两倍,有 2(a+b) = a+b+c+b,能够获得a=c(这个结论很重要!)。
咱们发现L=b+c=a+b,也就是说,从一开始到两者第一次相遇,循环的次数就等于环的长度。
咱们已经获得告终论a=c,那么让两个指针分别从X和Z开始走,每次走一步,那么正好会在Y相遇!也就是环的第一个节点。
在上一个问题的最后,将c段中Y点以前的那个节点与Y的连接切断便可。
如何判断两个单链表是否有交点?先判断两个链表是否有环,若是一个有环一个没环,确定不相交;若是两个都没有环,判断两个列表的尾部是否相等;若是两个都有环,判断一个链表上的Z点是否在另外一个链表上。
如何找到第一个相交的节点?求出两个链表的长度L1,L2(若是有环,则将Y点当作尾节点来算),假设L1<L2,用两个指针分别从两个链表的头部开始走,长度为L2的链表先走(L2-L1)步,而后两个一块儿走,直到两者相遇。
【总结】寻找环存在和环入口的方法:
用两个指针p一、p2指向表头,每次循环时p1指向它的后继,p2指向它后继的后继。若p2的后继为NULL,代表链表没有环;不然有环且p1==p2时循环能够终止。此时为了寻找环的入口,将p1从新指向表头且仍然每次循环都指向后继,p2每次也指向后继。当p1与p2再次相等时,相等点就是环的入口。