142.环形链表II
目录
- 142.环形链表II
- 一、哈希表
- 二、双指针
一、哈希表
和141题.判断链表是否有环类似,区别在于141题只要求判断链表中是否有环,该题则要求我们返回入环节点
一个非常直观的思路:遍历链表中的每个节点,并将它们记录下来
一旦遇到了此前遍历过的节点,就可以判定链表中存在环
当我们第一次发现有和集合中相同的结点时,也就找到了我们的入环节点,直接返回该节点即可,可以理解一下
借助HashSet可以实现
public ListNode detectCycle(ListNode head) {
ListNode pos = head;
Set<ListNode> visited = new HashSet();
while(pos!=null){
if(visited.contains(pos)){
return pos;
}else{
visited.add(pos);
}
pos = pos.next;
}
return null;
}
二、双指针
在之前141环形链表的题目中我们已经知道了如何判断一个链表是否有环,即通过快慢指针的方式
这道题在于,在判断出链表有环的前提下,要找到入环口
我们在基于链表已经是环形的情况下讨论:
假设:
从头结点到环形入口结点的结点数为x,
环形入口结点fast指针与slow指针相遇节点节点数为y,
从相遇节点再到环形入口节点的节点数为z
那么此时当slow指针和fast指针相遇时
slow指针走过的路程为:x+y
,fast指针走过的节点数:x+y+n*(y+z)
,n为fast指针在环内走了n圈才遇到slow指针,(y+z)为一圈内节点的个数A
因为fast指针是一步走两个节点,slow指针是一步走一个节点,所以fast指针走过的节点数 = slow指针走过的节点数*2
(x + y) * 2 = x + y + n (y + z)
整理得:x + y = n (y + z)
因为要找环形的入口,那么要求的是参数x,因为x表示从头结点到环形入口的距离
所以要求x ,将x单独放在左面:x = n (y + z) - y
,
再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z
注意这里n一定是大于等于1的,因为fast指针和slow指针相遇,至少是要走一圈的
这个公式说明什么?
当n等于1的时候(fast指针在环中转了一圈就和slow指针相遇),
n-1=0,此时x=z
这就意味着,从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点,那么当这两个指针相遇的时候就是环形入口的节点
也就是说,我们可以先找到相遇节点,在相遇节点设置一个index1指针
在起始位置设置一个index2指针,让index1和index2同时移动,每次移动一个节点,这两个指针相遇的地方就是环形入口的节点
如果n>1,无非就是fast指针转n圈之后才遇到slow指针
其实这种情况和n为1的时候效果是一样的,都可以通过这个方法找到环形入口的节点,只不过index1指针在环里多转了(n-1)圈罢了,然后再遇到index2,相遇点依然是环形的入口节点
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast!=null&&fast.next!=null){
slow = slow.next;
fast = fast.next.next;
if(slow==fast){//有环
ListNode index1 = slow;
ListNode index2 = head;
//两个指针,从头结点和相遇节点开始走
while(index1!=index2){
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}