題目描述
一個鏈表中包含環,請找出該鏈表的環的入口結點。
這個題目很經典:但是這種快慢指針的想法的原因我還沒有明白其中的由來;還有不明白爲什麼快指針只能走2步,或者又是什麼道理,沒懂;
但是清楚的明白一點就是,快慢指針相遇的點一定是在環內,那麼根據這個點在環內轉圈再次遇到即就能得到環的大小n,也就是環中結點的個數;接着。讓指針1先走環的n步.然後指針2從頭結點開始走。則再次相遇即就是環的入口。
哇哦真的思維很巧妙,等我明白其中的道理來!
開始根據劍指offer書上寫的算法如下:
package TestMyselfe;
public class Test56 {
public static class ListNode {
int value;
ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.value = val;
}
@Override
public String toString() {
return value + "";
}
}
//找到快慢指針相遇的結點在環內
public static ListNode MeetingNode(ListNode pHead)
{
if(pHead==null){
return null;
}
ListNode slow=pHead;
ListNode fast=pHead;
while (fast!=null && slow!=null) {
slow=slow.next;
fast=fast.next.next;
if(fast==slow){
return fast;
}
}
if(fast==null&&slow==null){
return null;
}
return fast;
}
/*
* 在有環的鏈表中找到環的入口結點的步驟:
* 1.指針p1和p2在初始化時都指向鏈表的頭結點;
* 2.由於環中有4個結點,指針p1先在鏈表上向前移動4步;
* 3.指針p1和p2以相同的速度在鏈表上向前移動直到它們相遇。
* 它們相遇的結點就是環的入口結點;
*/
public static ListNode EntryNodeOfLoop(ListNode pHead){
ListNode meetnode=MeetingNode(pHead);//環中相遇的結點是5
if(meetnode==null){
return null;
}
//得到結點中環的個數
int nodesinloop=1;
ListNode node1=meetnode;
//得到環中的結點個數
while(node1.next!=meetnode){
node1=node1.next;
++nodesinloop;//統計環中結點的個數累加
}
//分別兩個指針開始行走
node1=pHead;
//指針1先走
for (int i = 0; i < nodesinloop; i++) {
node1=node1.next;//讓node1先走nodesinloop步
}
ListNode node2=pHead;
//指針1和指針2同時一步步走,相遇即就是環的入口
while(node1!=node2){
node1=node1.next;
node2=node2.next;
}
return node1;
}
public static void main(String[] args) {
test01();
}
// 1->2->3->4->5->6
private static void test01() {
ListNode n1 = new ListNode(1);
ListNode n2 = new ListNode(2);
ListNode n3 = new ListNode(3);
ListNode n4 = new ListNode(4);
ListNode n5 = new ListNode(5);
ListNode n6 = new ListNode(6);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n6;
n6.next = n3;
System.out.println(EntryNodeOfLoop(n1));
}
// 1->2->3->4->5->6
// ^ |
// | |
// +--------+
}
後來發現還有更簡單的,意思就一樣,只不過就是不分開;先找到相遇點,接着一個從頭走,一個繼續走,一步一步,再次相遇即就是環的入口:
代碼如下:
package TestMyselfe;
public class NewTest56 {
public static class ListNode {
int value;
ListNode next;
public ListNode() {
}
public ListNode(int val) {
this.value = val;
}
@Override
public String toString() {
return value + "";
}
}
/*
* 1->2->3->4->5->6->3
*
*/
public ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead==null){
return null;
}
ListNode slow=pHead;
ListNode fast=pHead;
//快慢指針在鏈表的找到相遇的點肯定在環內
while(slow!=null&&fast.next!=null){
slow=slow.next;
fast=fast.next.next;
if(fast==slow){
break;
}
}
//鏈表無環就是空
if(slow==null||fast.next==null){
return null;
}
ListNode node1=pHead;
//環內相遇點開始
ListNode node2=slow;
//一步步再次相遇即就是環的入口
while(node1!=node2){
node1=node1.next;
node2=node2.next;
}
return node1;
}
}