1.有一个单向链表,链表当中有可能出现“环”,就像下图这样。如何用程序判断出这个链表是有环链表?
对于判断有环问题,能判断后面的节点和前面节点有没有相等的,这里相等是指是同一个对象,也就只能由一个后继节点,对于这种情况,我们可以设置两个节点,都从头节点开始,一个节点走两步,fast .next.next,一个节点走一步,即slow .next,如果有环,那么就会有fast==low的情况,也就能判断出来是否有环。
2.如何知道环的长度?
记录下问题1的相遇点,slow、fast从该点开始,再次相遇时slow所经过的节点数就是环的长度。从环上的任意一点开始,slow、fast再次相遇时slow经过的节点数就是环的长度,因为此时slow、fast起始距离为环长,速度差为1。选择问题1的相遇点为起始点是为了确保起始点为环上的一点。
3.如何找出环的连接点在哪里?
设问题1中的相遇点为m1,赋值p=m1,q=h,其中h为链表头结点,然后p,q每次1步向前运动,p,q再次相遇所在的位置就是环的入口节点(环的连接点)。
如图中所示,设链起点到环入口点间的距离为x,环入口点到问题1中fast与slow重合点的距离为y,又设在fast与slow重合时fast已绕环n周(n>0),且此时low移动总长度为s,则fast移动总长度为2s,环的长度为r。则
s + nr = 2s,n>0 ①
s = x + y ②
由①式得 s = nr
代入②式得
nr = x + y
x = nr - y
此时我们就能得出结论了,从重合点和头节点向下走,两者相等的时候为入口。
下面给出代码:问题是给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
需要先判断是否有环,如果有再判断环的入口结点,没有就返回null
public class Main {
public static void main(String[] args) {
ListNode node = new ListNode(1);
Solution solution = new Solution();
ListNode node1 = solution.EntryNodeOfLoop(node);
System.out.println(node1);
}
}
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode fast = pHead;
ListNode slow = pHead;
int flag = 0; //标记,0代表没环
while (fast != null) { //如果fast为空就不可能有环
if (fast.next == null || fast.next.next == null) {
return null;
}
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {//如果相同就说明有环,直接跳出循环
flag = 1;
break;
}
}
if (flag == 0) { //判断之后没环就返回
return null;
}
while (true) { //判断环的入口位置
if (pHead == slow) {
return slow;
}
pHead = pHead.next;
slow = slow.next;
}
}
}