一、题目
题目:一个链表中包含环,如何找出环的入口结点
二、举例
比如链表 1->2->3->4->5->6->3,那么链表中是存在环的,且环的入口是3
三、思想
其实该题目的编程并不难,难的是其数学的计算,首先看下面这张图:
1、总体思想:
一开始设置两个指针都指向表头,其中一个每次(一步)前进一个节点的叫p1,另外那个每次(一步)前进两个节点的
叫p2 。p1和p2同时走,当其中有一个遇到null,就证明链表没有环。如何某个时刻(假设走了n步之后),p1和p2指向的地址相
同,那么链表就是有环的。
在p1和p2重合后,设置一个p3指向表头,然后p1和p3每次同时行走一步,每步前进一个节点,等到p1
和p3重合时,重合的位置就
是环的入口。
2、☆计算公式☆:
(1)Head指的是初始化位置,L1是到链表环入口的长度,L2是整个环的长度,蓝色为重合P1和P2的重合点
(2)入口可重合点处的距离为a,如图所示,p1和p2同时走起,p2比p1块,所以p2在环内可能走了好几圈了
(3)当p1终于走到环里边的时候才能正好重合了
(4)推倒过程如下:
-------------------- L1+a=n #1 //n是p1走过的节点数
-------------------- L1+k*L2+a=2*n #2 //2*n这个是p2走过的节点数,其中的k表示p2可能在环里面走了k圈,k>=1
-------------------- 由#2式减去#1式,有:
-------------------- k*L2 = n #3
-------------------- 同时由#1和#3得到:
-------------------- L1+a = k*L2 #4
-------------------- 接着由#4就得到了如下式:
-------------------- L1 = k*L2 - a = (k-1)*L2 + (L-a)
(5)因为(L-a)表示的是交点与环入口的距离(从交点沿着行走方向到环入口),然后(k-1)是>=0的,因为p2在环中至少绕了一
圈,这样我们就发现:L1的长度 = 环长度的整数倍 + 交点与环入口的距离
☆☆☆☆☆☆☆也就是L1 = L2 - a,所以再让p1或者p2回来往前走L1步就正好重合了☆☆☆☆☆☆☆
四、程序
package 剑指offer;
public class Test56 {
public static void main(String args[]){
// 1->2->3->4->5
// ^ |
// | |
// +--+-----+
ListNodeEnter n1 = new ListNodeEnter(1);
ListNodeEnter n2 = new ListNodeEnter(2);
ListNodeEnter n3 = new ListNodeEnter(3);
ListNodeEnter n4 = new ListNodeEnter(4);
ListNodeEnter n5 = new ListNodeEnter(5);
n1.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
n5.next = n2;
System.out.println(findEnterOfList(n1));
}
public static int findEnterOfList(ListNodeEnter head){
//建立两个节点,p1和p2,p1的速度是p2的两倍
ListNodeEnter p1 = head;
ListNodeEnter p2 = head;
while(p1 !=null && p2.next != null){
p1 = p1.next.next;
p2 = p2.next;
if(p1 == p2){
break;
}
}
if(p1 == null || p2.next == null){
return -1;
}
//p1重新回到起点
p1 = head;
while(p1 != p2){
p1 = p1.next;
p2 = p2.next;
}
return p1.value;
}
}
class ListNodeEnter{
int value;
ListNodeEnter next;
public ListNodeEnter(int value){
this.value = value;
}
public String toString(){
return value+"";
}
}
--------------output--------------
2