1 题目链接
https://www.nowcoder.com/exam/oj/ta?page=1&tpId=13&type=13
2 题目
3 思路 & 代码
方案一 HashSet
遍历链表,把遇到的节点全都放入hashset中,每次在向set中添加元素之前,先检查一下set中是否已经包含此节点:
-
如果包含,则表示该节点就是链表的环的入口,直接返回该节点即可
-
如果链表遍历结束,还没有出现重复的节点,则说明该链表中没有环,返回null
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null) {
return null;
}
HashSet<ListNode> set = new HashSet<>();
ListNode tmp = pHead;
while (tmp != null) {
if (set.contains(tmp)) {
return tmp;
}
set.add(tmp);
tmp = tmp.next;
}
return null;
}
}
方案二 快慢指针
指定两个指针slow和fast,fast每次后移两个,slow每次后移一个。fast指针的速度是slow指针的两倍,相遇时所经历的时间是一样的,所以fast指针所走的距离时slow指针所走距离的了两倍。
假设:
- 从头节点到链表的环的入口的距离为x
- 从环的入口到相遇地点的距离是y
- 从相遇地点再走到环的入口的距离是z
- 在相遇之前,fast指针走了n个完整的环,slow指针走了m个完整的环,0<=m<n
由前面得出的fast和slow所走的距离的关系,可得出:
x + n(y + z) + y ------ fast指针走的距离
x + m(y + z) + y ------ slow指针走的距离
x + n(y + z) + y = 2[x + m(y + z) + y]
两边移项
x + y = (n - 2m)(y + z)
其中,
y + z刚好是环的大小;x + y是从头节点,经过环的入口到达相遇节点的距离;
也就是说,从头节点,经过环的入口到达相遇节点的距离
是环的大小
的整数倍
在fast和slow相遇以后,将其中一个指针移到头节点,然后两个指针以每次移动一个的速度前进
最终,两个指针会再次同时到达相遇地点,当两个节点再次到达相遇地点之前,肯定已经先进入环了,在环中共同走过一段,所以,两个指针第一次相等的节点,就是环的入口。
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
//空链表
if (pHead == null) {
return null;
}
ListNode fast = pHead;
ListNode slow = pHead;
while (fast != null && fast.next != null) {
//fast移动两次
fast = fast.next.next;
//slow移动一次
slow = slow.next;
//fast和slow相遇,说明链表中有环
if (fast == slow) {
//将fast指针移向头节点
fast = pHead;
while (fast != slow) {
//fast和slow移动一次
//再次相遇的地点就是环的入口
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
//如果while循环结束,说明fast指针先到达链表的结尾,链表中没有环,返回null
return null;
}
}