Question:
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Note: Do not modify the linked list.
Tips:
给定一个链表,如果链表中有环,返回环开始的结点。如果链表无环 返回null。
思路:
之前141题(链接),判断链表中是否有环,返回布尔类型的变量。本题还需要返回环开始的结点。解决过程分为以下几步。
(1)设置两个速度不同的指针,fast与slow。fast的速度是slow的二倍。
fast = fast.next.next;
slow = slow.next;
当两个指针相遇,证明链表存在结点,否则,fast指针先遇到null证明链表内无环。
(2)在两个指针相遇处切开,则变成了求两个单链表第一个公共结点的问题160(链接)。
slow路程:S1=L+a;(L链表无环部分长度,a为slow指针走过的环内的弧长)
fast路程:S2=L+a+n*k;(n表示环内结点长度,k表示 fast指针走过的环的圈数)
路程=速度*时间。slow的速度*2=fast的速度。相遇代表时间相等。则S1*2=S2。
2(L+a)=L+a+n*k; ==> L+a=n*k
==> L=n*k-a;
从上面的分析可以看出,链表中,不在环内的长度L=环的长度n - slow走过的弧长。
这时,新建一个指针,从head开始向后移动,相遇处的指针继续后移,当两个指针再次相遇,则该节点是环开始的第一个结点。
代码:
public ListNode detectCycle(ListNode head) { ListNode fast = head; ListNode slow = head; ListNode meet = null; while (fast != null) { if (fast.next != null) { fast = fast.next; if (fast.next != null) { fast = fast.next; slow = slow.next; if (fast == slow) { //fast与slow相遇 meet = slow; break; } } else return null; } else return null; } ListNode haha = head; //循环结束,两个指针均指向环开始的结点。 while (haha != meet) { haha = haha.next; meet = meet.next; } return meet; }
测试代码:
public static void main(String[] args) { ListNode head1 = new ListNode(1); ListNode head2 = new ListNode(2); ListNode head3 = new ListNode(3); ListNode head4 = new ListNode(4); ListNode head5 = new ListNode(5); ListNode head6 = new ListNode(6); head1.next = head2; head2.next = head3; head3.next = head4; head4.next = head5; head5.next = head6; head6.next = head3; L142LinkedListCycleII l142 = new L142LinkedListCycleII(); ListNode head = l142.detectCycle(head1); if(head!=null){ System.out.println(head.val); }else{ System.out.println("Null"); } }