一、题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
二、分析
方法一
找到环的入口结点,就是找到刚进入环的第一个结点,如下图,找入口结点就是找第一个发生重复的结点,那么我们又可以借鉴之前两个习题(中等20、中等22)的思想,也就是用一个List集合,如果集合中不包含结点,就添加。如果集合中包含结点,那么就跳出循环,返回该结点,该目标结点即为第一个重复的结点,也就是入口结点,由于要遍历整个链表,故其时间复杂度为O(n2)。
实现代码如下:
import java.util.List;
import java.util.ArrayList;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode targetNode = null;
if(pHead == null || pHead.next ==null){
return null;
}
List<ListNode> list = new ArrayList<ListNode>(); //集合中存放结点
list.add(pHead); //添加头结点
targetNode = pHead.next;
// 判断是否存在环,即list中是否存在重复结点
while(!list.contains(targetNode)){
list.add(targetNode);
targetNode = targetNode.next;
}
return targetNode;
}
}
方法二
快慢指针(需要理解,但这个算法时间复杂度更低)
方法分两个步骤处理
1、设置两个指针,一个为快指针fast,一个为慢指针low,快指针一次走两步,慢指针一次一步,最后肯定会遇上。第一次遇上我们记为相遇点,此时快慢指针都指向这个相遇点。
2、fast继续指向这个相遇点,但是让low指针指向链表头结点,此时快慢指针都一起每次走一步,最终他们会在入口结点相遇。
为什么这样会在入口结点相遇呢?我们带着这个问题进行推理分析一下,如下图
a,b,c分别为三个结点之间的距离
我们先讨论到相遇点,fast与low两指针走过的路程:
快指针路程 = a+(b+c)k+b ,k≥1,这表示至少一圈环,b+c为一个环的长度
慢指针路程 = a+b
由于快指针步长等于慢指针2倍,则
a+(b+c)k+b = 2(a+b),化简得a = (k-1)(b+c)+c,该式子意思是:
链表头到入口结点的距离 = 相遇点到入口结点的距离+(k-1)圈环长度
因此,在找到相遇点后,还需要将low重置到表头,继续每次走一步,这样得话,两个最后一定会在入口结点相遇,返回该结点即可
实现代码如下:
import java.util.List;
import java.util.ArrayList;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead == null|| pHead == null)
return null;
ListNode fast = pHead;
ListNode low = pHead;
//找相遇点
while(fast != null && fast.next != null){
fast = fast.next.next;
low = low.next;
if(fast == low)
break;
}
//相遇点非空
if(fast == null|| fast.next == null)
return null;
low = pHead; //链表头出发
while(fast != low){
fast = fast.next;
low = low.next;
}
return low;
}
}