带环的链表

本文深入探讨了带环链表的基本概念、核心算法及其实际应用,包括如何判断链表是否有环、找到环的入口点和计算环的长度。通过详细解释算法原理并提供Java代码实现,为开发者提供了全面理解带环链表问题的解决方案。

走一步的指针叫slow,走两步的叫fast。

相遇的时候,slow共移动了s步,fast共移动了2s步,这个是显而易见的。

定义a如下: 链表头移动a步到达入口点。

定义x如下: 入口点移动x步到达相遇点。

定义r如下: 从环中的某一点移动r步,又到达的这一点,也就是转了一圈的意思。

定义t如下: 从相遇点移动到入口点的移动步数

定义L如下: 从链表头移动L步,又到达了相遇点。也就是遍历完链表之后,通过最后一个节点的指针,又移动到了链表中的某一点。

其中L = a + r  =  a + x + t

那么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 = (n-1)*r + t

也就是说,从表头到入口点的距离,等于从相遇点继续遍历又到表头的距离。

所以,从表头设立一个指针,从相遇点设立一个指针,两个同时移动,必然能够在入口点相遇,这样,就求出了相遇点。

java代码实现关于带环链表的一些基本操作 大家可以自己测试

class Node<T extends Comparable<T>> implements Comparable<Node<T>>{
private T data;
private Node<T> next;

public Node(){

}
public Node(T data){
this.data=data;
this.next=null;
}



public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node<T> getNext() {
return next;
}
public void setNext(Node<T> next) {
this.next = next;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.hashCode());
return result;
}


@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (data == null) {
if (other.data != null)
return false;
} else if (!data.equals(other.data))
return false;
return true;
}
@Override
public int compareTo(Node<T> o) {
return this.data.compareTo(o.getData());
}
}


// 判断一个单链表中是否有环: hasCycle

/*
* 设置两个指针(fast, slow),初始值都指向头,slow每次前进一步,fast每次前进二步,
* 如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇。
* (当然,fast先行头到尾部为NULL,则为无环链表)
* */
public boolean hasCycle(){
return this.hasCycle(this.head);
}
private  boolean hasCycle(Node<T> head){  
       boolean flag=false;  
       Node<T> slow=head;  
       Node<T> fast=head;  
       while(fast!=null&&fast.getNext()!=null){  
           slow=slow.getNext();
           fast=fast.getNext().getNext();  
           if(slow.equals(fast)){  
               flag=true;  
               break;  
           }  
       }  
       return flag;  
   }  
 
/*
* 找到环的入口点
*  假设单链表的总长度为L,头结点到环入口的距离为a,环入口到快慢指针相遇的结点距离为x,环的长度为r,
*  慢指针总共走了s步,则快指针走了2s步。
*  另外,快指针要追上慢指针的话快指针至少要在环里面转了一圈多(假设转了n圈加x的距离),得到以下关系:
    s = a + x;
    2s = a + nr + x;
    =>a + x = nr;
    =>a = nr - x;
*/
 
//寻找环的入口点
private Node<T> FindLoopPort(Node<T> head)
{
    Node<T> slow=head,fast=head;
    //得到相遇点
    while(fast!=null && fast.getNext()!=null)
    {
        slow=slow.getNext();
        fast=fast.getNext().getNext();
        if(slow.equals(fast))
            break;
    }
    if(fast==null||fast.getNext()==null)
        return null; //无环
    //slow指向开头,fast在相遇点
    //得到入口点
    slow=head;
    while(!slow.equals(fast)){
        slow=slow.getNext();
        fast=fast.getNext();
    }
    return slow;

/* 
 * 如何知道环的长度
 * 记录下问题1的碰撞点p,slow、fast从该点开始,再次碰撞所走过的操作数就是环的长度s。
 * */
public int getCycleLength(){
return this.getCycleLength(this.head);
}
private int getCycleLength(Node<T> head){
Node<T> p=this.FindLoopPort(head);
if(p==null) return 0;//无环
Node<T> slow=p;
Node<T> fast=p;
int count=0;
while(fast!=null&& fast.getNext()!=null){
slow=slow.getNext();
fast=fast.getNext().getNext();
count++;
if(slow.equals(fast)){
break;
}
}
return count;
}
//带环链表的长度
public int getCycleListLength(){
return getCycleListLength(this.head);
}
private int getCycleListLength(Node<T> head){
Node<T> pnode=this.FindLoopPort(head);
if(pnode==null){
return this.size(head);
}
int count=0;//记录头结点到接入点的距离
Node<T> node=head;
while(!node.equals(pnode)){
count++;
node=node.getNext();
}
int count1=this.getCycleLength(head);//得到环的长度
return count+count1;
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值