1. 问题描述:
给定一个有环的链表,实现一个算法返回环路的开头节点
有环链表的定义:在链表中某个节点的next元素指向在它前面出现过的节点,则表明该链表存在环路
要求:不能够使用额外的空间来进行记录
2. 题目与之前不一样的是不能够开辟额外的空间来进行记录,所以我们是不能够使用HashSet来判断哪个节点存在着重复,只能通过另外的方法来进行求解
这里使用到的又是一个非常经典的做法,定义两个指针,快指针和慢指针
假设慢指针s走k步到了有环的节点,因为快指针f每次走两步,所以与有环节点的距离为k,那么这个时候应该是f追s,有环节点的长度为l,所以s,f另外之间的距离为l - k,所以f要走多l - k的距离就可以追上s,与s相遇,由于每走一步s与f的距离就会减少1,所以当s走l - k步的时候那么两者就相遇了,这个时候相遇的点是关键,明显可以看出相遇的点与有环的点相距k的距离
而头结点到有环节点的距离也是k,那么只要s与头结点同时以同样的速度移动那么当他们相遇的时候该节点就是有环的节点了
我们可以在循环中判断两个指针是否相等,相等的时候跳出循环然后s与头结点同时以同样的速度移动那么当他们相遇的时候该节点就是有环的节点了
3. 具体的代码如下:
public class Main{
private static class ListNode{
private ListNode next;
int value;
public ListNode(int value) {
super();
this.value = value;
}
}
public static void main(String[] args) {
ListNode node = new ListNode(1);
node.next = new ListNode(2);
node.next.next = new ListNode(3);
node.next.next.next = new ListNode(4);
node.next.next.next.next = new ListNode(5);
//创建环路
node.next.next.next.next.next = node.next.next.next;
ListNode p = beginOfCircle(node);
System.out.println(p.value);
}
private static ListNode beginOfCircle(ListNode node){
ListNode s = node;
ListNode f = node;
while(true){
//一次一两步
s = s.next;
//一次走两步
f = f.next.next;
//表示两者相遇了此时应该跳出循环
if(s == f) break;
}
ListNode p = node;
while(p != s){
p = p.next;
s = s.next;
}
//循环退出的时候那么s与p指向的节点就是有环链表的起点
return p;
}
}