链表中环的入口结点

Posted by DH on July 25, 2017

题目

一个链表中包含环,请找出该链表的环的入口结点。

分析一

这个题目应该有蛮多解法的。第一种是利用hashset,那么hashset有什么特点呢?说道hashset第一反应就应该是不能插入重复的元素。

因此,我们从头开始插入,假如有环的话,插入就会失败,这个时候就是第一个重复的元素,也就是环的开头。

代码

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
import java.util.HashSet;
public class Solution {

    HashSet<ListNode> hashSet = new HashSet<>();
	 
	public ListNode EntryNodeOfLoop(ListNode pHead)
	  {
			if (pHead == null) {
				return null;
			}
			
			while(pHead != null){
				boolean insertSuccess = hashSet.add(pHead);
				if (insertSuccess == false) {
					return pHead;
				}
				
				pHead = pHead.next;
			}
			
			return null;
    }
}	

分析二

设置两个指针,p1和p2,分别指向链表头,p1每次按照1的步数往前,p2每次按照2的步数向前,当p1和p2相遇的时候,p2比p1多走了k圈。

整个过程如下图所示:(网上的图)

有了这个图就容易分析了

假设AC表示从起点到环入口的距离,CB表示从环入口到相遇点的距离,BC表示相遇点到环入口的位置,CLenght表示环的长度

那么p1走的距离是AC + (m * CLenght) + CB

p2走的距离是:AC + (n*CLenght) + CB

并且2(AC + (m * CLenght) + CB) = AC + (n*CLenght) + CB

那么,AC = (2m-n)CLenght + BC

这就说明,如果将一个指针退回到链表开头,两个指针一起前移,最终会在环入口点相遇。

代码二

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
import java.util.HashSet;
public class Solution {


	 
	public ListNode EntryNodeOfLoop(ListNode pHead)
	  {
			 if (pHead == null || pHead.next == null) {
			return null;
		}
		 
		 ListNode p1 = pHead;
		 ListNode p2 = pHead;
		 
		 while(p2 != null && p2.next != null){
			 p1 = p1.next;
			 p2 = p2.next.next;
			 
			 if (p1 == p2) {
				p1= pHead;
				while ( p1 != p2) {
					p1 = p1.next;
					p2=p2.next;
				}
				if (p1 == p2) {
					return p1;
				}
			}
			
         }
         return null;
    }
}