本文解决三个问题:
1.单链表是否有环?
2.有则输出环的长度?
3.找到环的入口节点?
分析:
定义两个指针fast 和slow,fast每次向后移动两个节点,slow每次想后移动一个节点。
1.如果没有环,则fast首先到达链表结尾;
2.链表有环的情况下:fast与slow两次相遇,slow中间走过的节点处即为环的长度;
3.找环的入口节点稍微复杂点,有如下的推导过程:
相遇的时候,slow共移动了s步,fast共移动了2s步。
定义a如下: 链表头移动a步到达入口点。
定义x如下: 入口点移动x步到达相遇点。
定义r如下: 环的长度。
定义L如下: 链表总长度为L。
其中L = a + r
那么slow和fast相遇了,fast必然比slow多走了n个圈,也就是 n*r 步,那么
s = a + x
2s = s + n*r , 可得 s = n*r
将s=a+x,带入s =n*r,可得 a+x = n*r, 也就是 a+x = (n-1)*r + r
从表头移动到入口点,再从入口点移动到相遇点,也就是移动了整个链表的距离,即是 L = a + r , 所以r = L - a
所以 a+x = (n-1)*r + L - a , 于是 a = (n-1)*r + L - a - x
得到:从表头到入口点的距离,等于从相遇点到入口点的距离。
所以,从表头设立一个指针,从相遇点设立一个指针,两个同时移动,必然能够在入口点相遇,这样,就求出了相遇点。
上面三个问题的java解决代码:
方法一:一次性求解
public class ExistCircle {
static int id = 1;
public Node existCircle(Node head){
Node fast = head;
Node slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
//输出环长
while(fast == slow) {
int len = 1;
fast = fast.next.next;
slow = slow.next;
while (fast != slow) {
len++;
fast = fast.next.next;
slow = slow.next;
}
System.out.println("The length of circle is:" + len);
//输出环入口节点
fast = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
id++;
}
return slow;
}
}
return null;
}
public static void main(String[] args) {
Node head=new Node(3);
Node node1=new Node(6);
Node node2=new Node(8);
Node node3=new Node(5);
Node node4=new Node(2);
Node node5=new Node(7);
head.next=node1;
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
node5.next=node3;
Node port = new ExistCircle().existCircle(head);
System.out.println("环入口为第" + id + "个节点:" + port.data);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
方法二:分开求解:
package listnode;
/**
* @author Gavenyeah
* @date Time: 2016年5月18日上午11:36:40
* @des:
*/
public class ExitCircle {
static int id = 1;
public static void main(String[] args) {
//测试
Node head=new Node(3);
Node node1=new Node(6);
Node node2=new Node(8);
Node node3=new Node(5);
Node node4=new Node(2);
Node node5=new Node(7);
head.next=node1;
node1.next=node2;
node2.next=node3;
node3.next=node4;
node4.next=node5;
node5.next=node3;
new ExitCircle().exitCircle(head);
Node port = new ExitCircle().findLoopPort(head);
System.out.println("环入口为第" + id + "个节点:" + port.data);
}
//环入口节点
//环的入口节点到快慢指针相遇的距离 与 链表头节点到环入口节点的距离相等
public Node findLoopPort(Node head){
Node slow = head, fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
fast = head;
while(head != slow){
id++;
head = head.next;
slow = slow.next;
}
return slow;
}
}
System.out.print("NoLoop !");
return null;
}
public boolean exitCircle(Node head){
Node fast = head;
Node slow = head;
while(fast != null && fast.next != null){//判断是否由环,注意fast.next = null的情况
fast = fast.next.next;
slow = slow.next;
if(fast == slow){//存在环
int count = 0;
while(true){
count ++;
fast = fast.next.next;
slow = slow.next;
if(fast == slow){//快慢指针在第二次相遇,这个点肯定是第一次相遇的点
System.out.println("环的长度:" + count);
return true;
}
}
}
}
System.out.println("false!");
return false;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75