【算法】鏈表中環的入口結點

題目描述

一個鏈表中包含環,請找出該鏈表的環的入口結點。
這個題目很經典:但是這種快慢指針的想法的原因我還沒有明白其中的由來;還有不明白爲什麼快指針只能走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;
	 }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章