环形链表II
题目描述
给定一个可能包含一个环的链表。请验证这个链表是否有环,如果有请返回入环的第一个节点。如果无环返回null。
实例:
3-->2-->0-->-4--.
^ |
|___________|
返回:节点2
节点:
public class ListNode {
int val;
ListNode next;
public ListNode(int x){
this.val = x;
next = null;
}
}
解法一:哈希表记录
思路:
检验是否有环:首先用两个遍历速度分别为1和2的指针去遍历链表,如果两个指针能相遇即有环,如果速度快的指针遍历到尾了,则无环。
寻找入环的点:用一个HashSeti记录慢指针走过的节点,如果遇到重复的则返回该节点
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode front = head;
ListNode pre = head;
Set<ListNode> set = new HashSet<>();
set.add(pre);
//检查是否存在环
while (true){
if(pre.next !=null){
pre = pre.next;
if(set.contains(pre)){
return pre;
}
//检查中顺便记录节点
set.add(pre);
}
if(front.next!=null && front.next.next !=null){
front = front.next.next;
}else {
return null; //到头了,无环,直接返回
}
if(pre == front){
break;
}
}
//能跳出上面的循环说明链表有环
while (true){
//记录节点
pre = pre.next;
//判断是否节点是否已经存在
if(set.contains(pre)){
return pre;
}
set.add(pre);
}
}
}
解法二:
检验是否有环:首先用两个遍历速度分别为1和2的指针去遍历链表,如果两个指针能相遇即有环,如果速度快的指针遍历到尾了,则无环。
**寻找入环节点:**用数学公式稍加推导
3-->2-->0-->-4--.
^ |
|___________|
假设3-->2这段入环的路程长度为y
已知两个指针相遇的节点为-4,则设2-->-4的长度为s1 , -4-->2的长度为s2
慢指针速度为x ,快指针速度为2x
则:
1. y+s1+n(s1+s2) = xt (n为圈数,t为时间)
2. y+s1+k(s1+s2) = 2xt (k为圈数,t为时间)
合并
y = (k-2n-1)(s1+s2)+s2
结论:y = s2+g(s1+s2) (g为圈数),即y的长度是环长的整数倍加s2,所以检测出碰撞点之后,
就可以通过一个头节点和一个碰撞节点向中间逼近就可以算出入点
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null){
return null;
}
ListNode front = head;
ListNode pre = head;
while (true){
if(pre.next !=null){
pre = pre.next;
}
if(front.next!=null && front.next.next !=null){
front = front.next.next;
}else {
return null;
}
if(pre == front){
break;
}
}
pre = head; //将慢节点复位
while (pre != front){
pre = pre.next;
front=front.next; //两个节点速度调为1
}
return pre;
}
}