标签:链表,hash
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
解题思路
-
一种方法是用 hashmap来存储和查找节点;
另一种方法是双指针法。
注意复习HashSet和HashMap的插入查询等用法! -
另一种就是著名的快慢指针法。在牛客网上找了个好理解的解释:
-
第一步,找环中相汇点。分别用p1,p2指向链表头部,p1每次走一步,p2每次走二步,直到p1==p2找到在环中的相汇点。
-
第二步,找环的入口。接上步,当p1 == p2时,设p2所经过节点数为2x, p1所经过节点数为x,设环中有n个节点,p2比p1多走一圈有2x=n+x;(假设n很大,只多走了一圈) n=x; 可以看出p1实际走了一个环的步数,再让p2指向链表头部,p1位置不变,p1,p2每次走一步直到p1==p2; 此时p1指向环的入口(可以画图,一目了然)。
-
所以,我们设置两个指针,一个是快指针fast,一个是慢指针slow,fast一次走两步,slow一次走一步,如果单链表有环那么当两个指针相遇时一定在环内。此时将一个指针指到链表头部,另一个不变,二者同时每次向前移一格,当两个指针再次相遇时即为环的入口节点。如果fast走到null则无环。
-
参考代码
- HashSet
import java.util.HashSet;
/*
* 方法一:hashset存每个节点,第一个重复的就是环入口,空间复杂度O(n)
*/
public static ListNode EntryNodeOfLoop(ListNode pHead) {
//可以不写
if(pHead == null)
return null;
HashSet<ListNode> set = new HashSet<>();
while(pHead != null && pHead.next != null){
if(set.contains(pHead))
return pHead;
else {
set.add(pHead);
}
pHead = pHead.next;
}
return null;
}
- 方法二,快慢指针
正常写法:
public static ListNode EntryNodeOfLoop1_1(ListNode pHead) {
if(pHead == null || pHead.next == null)
return null;
ListNode fast = pHead;
ListNode slow = pHead;
while(fast != null && fast.next != null) {
// 根据结论,如果相遇,fast回到头结点,两个一起走
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
break;
}
}
//判空
if(fast == null || fast.next == null)
return null;
else {
fast = pHead;
while(fast != slow) {
fast = fast.next;
slow = slow.next;
}
}
return slow;
}
改进写法:
public static ListNode EntryNodeOfLoop1(ListNode pHead) {
if(pHead == null || pHead.next == null)
return null;
ListNode fast = pHead;
ListNode slow = pHead;
while(fast != null && fast.next != null) {
//这两句在前,不然结果错误,想想为什么?
//因为初始时是相等的啊
fast = fast.next.next;
slow = slow.next;
// 根据结论,如果相遇,fast回到头结点,两个一起走
if(fast == slow) {
fast = pHead;
while(fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
//能运行到这里,说明没有环
return null;
}