题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
经典的快慢指针的题。一快一慢,快的追上慢的就说明有环,快的没追到慢的,就说明快的会跑到链表尾部,先为Null。通过这个步骤可以判断是否有环。但是要找到环的入口节点就得具体分析了。
解法1:
思路
1.先确定是否有环。
2.确定环中结点的数目n:指针走一圈,边走边计数。
3.找到环的入口:两个指针都从头结点开始,一个先走n步,两个相差为n的指针同时运动,最终会在入口相遇。设单链部分长为x,环长为n,一个走x步,另一个则走了n+x步,所以正好在入口处相遇。
public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { ListNode node = meetingNode(pHead);//拿到相遇的节点 if(node == null) return null; //得到环中的节点个数 int count =1 ; ListNode p1 = node; while(p1.next != node){ p1 = p1.next; count++; } //p1先走N步 p1 = pHead; for(int i=0; i<count;i++){ p1 = p1.next; } // 同时移动 ListNode p2 = pHead; while(p1!=p2){ p1 = p1.next; p2 = p2.next; } return p1; } //返回换相遇的节点 public ListNode meetingNode(ListNode head) { if(head==null) return null; ListNode slow = head.next; if(slow==null) return null; ListNode fast = slow.next; while (slow != null && fast != null) { if(slow==fast) return fast; slow=slow.next; fast=fast.next.next; } return null; } }
解法2:
思路:这个解法需要动脑子找规律,像我这种不会画图的人,感觉语言很难解释清楚。我就推导一下过程哈,其实不难,看不懂下列推导的可以看图解(其他大佬的哈)
一共设置3个指针,一快两慢。其中让一快一慢去做相遇的动作(快是慢的两倍速度)。当快慢指针相遇时,设快指针走了a步,慢指针走了b步(a=2b)。其次设单链部分长为x,环长为y,快指针在环上绕了n圈还多k步,慢指针绕环走了m圈还多k步。将a、b写成和链表长度有关的方程,有a=x+ny+k
,b=x+my+k
。消去a和b,得到x = ny - 2my - k
。令c = ny-2my = (n-2m)y
,也就是说,c是环长y的整数倍。注意,慢指针此时停在离环入口距离为k的地方,而再走y-k步就是回到了环的起始点,那么如果不介意的话,请让这个慢指针多走几圈,走的距离是c-k这么多,还是能回到入口点。而这个距离正好等于x即单链部分长度。
c-k =(n - 2m - 1)*y +(y - k)
可以看到c-k确实能让慢指针回到原点,只不过多走n-2m-1圈
这就说明当第三个指针从原点开始走到入口时,慢指针同时从K处动身,最终两个慢指针将会在入口处相遇。
public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { if( pHead==null) return null; ListNode fast= pHead; ListNode slow= pHead; ListNode slow2= pHead; while(fast.next!=null && fast.next.next!=null){ fast=fast.next.next; slow=slow.next; if(fast==slow){ while(slow2!=slow){ slow2=slow2.next; slow=slow.next; } return slow2; } } return null; } }
解法3:
想到使用Set集合,将节点不断放入set集合,当某个节点重复出现时,就说明这个节点是环的节点。而第一次出现的重复节点就是环的入口节点
import java.util.HashSet; public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead){ if(pHead == null) return null; HashSet<ListNode> nodes = new HashSet<>(); while (pHead != null) { if(nodes.contains(pHead)){ return pHead; }else{ nodes.add(pHead); pHead = pHead.next; } } return null; } }