本来想学习一下找环的入口的数学知识,结果实在是想不通,还是先放一放吧,直接上代码了:
package mypackage;
//测试
public class MyJava {
//节点类
private static class Node<T> {
T data;
Node next;
//构造方法
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
// 判断是否是环形链表
public static boolean isCircle(Node node){
// 定义快慢指针
Node fast=node;
Node slow=node;
// 快指针不是null,且下一个节点也不是null的情况下
// 让快指针每次走两步,慢指针每次走一步
// 如果快慢指针能相遇,那么就证明链表是环形的
while((fast!=null)&&(fast.next!=null)){
// 两个next就是模拟两步
fast=fast.next.next;
slow=slow.next;
if (fast.equals(slow)){
// return直接结束isCircle方法,自然就具有退出循环的作用
System.out.println("快慢指针相遇点:"+fast.data);
return true;
}
}
// 跳出循环表示有结束节点,不是环形的
return false;
}
//找出环的入口
// 先定义一个临时Node指针,当判断是环形链表时,此时快慢指针相遇,
// 这个时候将temp指针赋值为第一个节点,就是entrance方法传入的参数,且让temp以和慢指针一样的速度移动
// 当慢指针和temp相遇时,共同指向的节点就是环的入口
public static Integer entrance(Node node){
// 定义快慢指针
Node fast=node;
Node slow=node;
Node temp=null;
// 快指针不是null,且下一个节点也不是null的情况下
// 让快指针每次走两步,慢指针每次走一步
while((fast!=null)&&(fast.next!=null)){
// 两个next就是模拟两步
fast=fast.next.next;
slow=slow.next;
if (fast.equals(slow)){
// 这时链表已经判断为环形的
// temp赋值完后,continue直接进入下一次循环,跳过之后的代码
temp=node;
continue;
}
// 如果temp和slow相遇,返回temp节点
if (temp!=null){
temp=temp.next;
if (temp.equals(slow)){
return (Integer) temp.data;
}
}
}
// 如果跳出循环,说明不是环形队列,自然就没有入口
return null;
}
public static void main(String[] args) {
// 创建节点,采用这样的方式创建,节点数改为偶数个
Node<Integer> a=new Node<Integer>(1,null);
Node<Integer> b=new Node<Integer>(2,null);
Node<Integer> c=new Node<Integer>(3,null);
Node<Integer> d=new Node<Integer>(4,null);
Node<Integer> e=new Node<Integer>(5,null);
Node<Integer> f=new Node<Integer>(6,null);
Node<Integer> g=new Node<Integer>(7,null);
// 节点指向性
a.next=b;
b.next=c;
c.next=d;
d.next=e;
e.next=f;
f.next=g;
// 添加环形,环形入口节点为c
g.next=c;
// 调用方法
System.out.println("是否是环形链表:"+isCircle(a));
System.out.println("环的入口元素值:"+entrance(a));
}
}
关于找入口的数学原理,参考以下文章,我没看太懂:
[寻找环链表入口点] 快慢指针数学原理剖析