Q:判断链表中是否存在环的相关问题

问题:如何判断一个单向链表中是否存在环?

例如:

链表中存在环(B-->D):

     <-- <--^
    |       |
    v       |
A-->B-->C-->D

链表中不存在环:

A-->B-->C-->D-->E-->F

解题思路:

??从一个实际的生活场景出发,两个人,在一个环形的操场上跑步的时候,如果有一个人跑得比另一个人还要快,那么,在n圈之后,这两个人总会在操场上的某个点相遇。将操场类比于链表中存在的环路径,将两个人看成两个指针,那么这道题的解题思路就自然而然的出来了。

具体步骤如下:

  1. 初始化两个指针a,b,同时指向链表的开头
  2. a指针走一步,b指针走两步(我们姑且将a指针称为慢指针,将b指针称为快指针)
  3. 重复步骤2,直到b指针无法继续往下走两步或者a,b指针相遇
  4. 当b指针无法继续往下走两步的时候,说明链表中不存在环,b指针即将走到链表的末尾端。
  5. 当a,b指针相遇,说明链表中存在环,因为只有存在环的情况,a,b指针才有可能会在环中的某个点相遇

具体代码如下:

/**
 * @author 学徒
 *
 * 用于判断链表中是否存在环
 *
 */
public class CycleLinkedList {
    /**
     * 循环链表中的节点类
     */
    static class Node<T>{
        //节点值
        T value;
        //节点的下一个节点的指针
        Node<T> next;
        public Node(T value){
            this(value,null);
        }
        public Node(T value,Node next){
            this.next=next;
            this.value=value;
        }
    }

    /**
     * 用于判断该链表中是否存在着环
     * @param head 链表的头节点
     * 当存在环时,返回true,否则返回false
     */
    public boolean judge(Node head) {
        if(head==null){
            return false;
        }
        //两个指向头结点的指针
        Node a=head,b=head;
        while(true){
            if(b.next==null||b.next.next==null){
                return false;
            }
            a=a.next;
            b=b.next.next;
            if(a==b){
                return true;
            }
        }

    }

    public static void main(String[] args){
        Node<String> a=new Node<String>("A");
        Node<String> b=new Node<String>("B");
        Node<String> c=new Node<String>("C");
        Node<String> d=new Node<String>("D");
        a.next=b;
        b.next=c;
        c.next=d;
        d.next=b;
        CycleLinkedList list=new CycleLinkedList();
        boolean result=list.judge(a);
        System.out.println("链表中环的结果:"+result);
    }
}

??从上面的链表中是否存在环的问题,可以延伸出与链表中是否存在环相关的另一个问题

问题: 链表中构成环的元素的个数应该如何计算?

??对于这个问题,我们稍微沿着解决上面的链表存在环的问题的思路继续往下想,当链表中存在环的时候,快慢指针相遇,那么这个时候,我们只需要让快指针停留在相遇的位置,让慢指针再次走一遍,边走边记录步数,当快慢指针再次相遇的时候,慢指针所走的步数,便是构成环的链表的环中元素个数。

我们只需要稍微修改下上面的代码即可,具体代码如下:

/**
 * @author 学徒
 *
 * 用于判断链表中是否存在环
 *
 */
public class CycleLinkedList {
    /**
     * 循环链表中的节点类
     */
    static class Node<T>{
        //节点值
        T value;
        //节点的下一个节点的指针
        Node<T> next;
        public Node(T value){
            this(value,null);
        }
        public Node(T value,Node next){
            this.next=next;
            this.value=value;
        }
    }

    /**
     * 用于判断该链表中是否存在着环
     * @param head 链表的头节点
     * 当存在环时,返回环中元素个数,否则返回0
     */
    public int judge(Node head) {
        if(head==null) {
            return 0;
        }
        //两个指向头结点的指针
        Node a=head,b=head;
        while(true){
            //当出现该情况的时候,说明无环
            if(b.next==null||b.next.next==null){
                return 0;
            }
            a=a.next;
            b=b.next.next;
            //当其存在环
            if(a==b){
                int number=1;
                a=a.next;
                while(a!=b){
                    a=a.next;
                    number++;
                }
                return number;
            }
        }

    }

    public static void main(String[] args){
        Node<String> a=new Node<String>("A");
        Node<String> b=new Node<String>("B");
        Node<String> c=new Node<String>("C");
        Node<String> d=new Node<String>("D");
        a.next=b;
        b.next=c;
        c.next=d;
        d.next=b;
        CycleLinkedList list=new CycleLinkedList();
        int result=list.judge(a);
        System.out.println(result);
    }
}

??顺着上面的问题继续的往下想,我们可以延伸出另一个问题

问题:我们是否可以得到第一个进入该链表的环的节点的元素?

??对于该问题,我们可以通过以下的方式得到该节点。

  1. 将快指针重新指向链表的头节点
  2. 快指针和慢指针同时走,当快指针和慢指针相遇时,该节点便是链表中第一个进入环的节点

ps:以上只是一个结论的步骤总结。实际上,可以通过分析得到,在环中,快慢指针第一次相遇时的节点位置与进入环的第一个节点的顺时针方向的距离同链表头节点到进入环中第一个节点的位置的距离相等。

具体代码如下:

/**
 * @author 学徒
 *
 * 用于判断链表中是否存在环
 *
 */
public class CycleLinkedList {
    /**
     * 循环链表中的节点类
     */
    static class Node<T>{
        //节点值
        T value;
        //节点的下一个节点的指针
        Node<T> next;
        public Node(T value){
            this(value,null);
        }
        public Node(T value,Node next){
            this.next=next;
            this.value=value;
        }
    }

    /**
     * 用于判断该链表中是否存在着环
     * @param head 链表的头节点
     * 当存在环时,返回环中元素个数,否则返回0
     */
    public Node judge(Node head) {
        if(head==null) {
            return null;
        }
        //两个指向头结点的指针
        Node a=head,b=head;
        while(true){
            //当出现该情况的时候,说明无环
            if(b.next==null||b.next.next==null){
                return null;
            }
            a=a.next;
            b=b.next.next;
            //当其存在环
            if(a==b){
                b=head;
                while(a!=b){
                    a=a.next;
                    b=b.next;
                }
                return b;
            }
        }

    }

    public static void main(String[] args){
        Node<String> a=new Node<String>("A");
        Node<String> b=new Node<String>("B");
        Node<String> c=new Node<String>("C");
        Node<String> d=new Node<String>("D");
        a.next=b;
        b.next=c;
        c.next=d;
        d.next=b;
        CycleLinkedList list=new CycleLinkedList();
        Node result=list.judge(a);
        System.out.println(result.value);
    }
}

主目录:

回到目录|·(工)·)

原文地址:https://www.cnblogs.com/MyStringIsNotNull/p/9125252.html

时间: 2024-11-07 07:58:20

Q:判断链表中是否存在环的相关问题的相关文章

LeetCode -- 判断链表中是否有环

思路: 使用两个节点,slow和fast,分别行进1步和2步.如果有相交的情况,slow和fast必然相遇:如果没有相交的情况,那么slow或fast必然有一个为null 相遇时有两种可能:1. 只是节点相交的情况,即:slow == fast但是 slow.next != fast.next2. 链表中存在环,即slow == fast 而且 slow.next == next 实现代码: public bool HasCycle(ListNode head) { // - for null

如何判断链表中是否有环

1.如何判断是否有环?如果有两个头结点指针,一个走的快,一个走的慢,那么若干步以后,快的指针总会超过慢的指针一圈. 2.如何计算环的长度?第一次相遇(超一圈)时开始计数,第二次相遇时停止计数. 3.如何判断环的入口点:碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点.头指针开始走,相遇的那个点就是连接点. 为什么呢?需要一个简单的计算过程: (1)当fast与slow相遇时,show肯定没有走完链表,而fast已经在还里走了n(n>= 1)圈.假设slow走了s步,那么fast走

剑指offer——判断链表中是否有环

题目链接:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. 解题思路: //左神讲的 //先说个定理:两个指针一个fast.一个slow同时从一个链表的头部出发 //fast一次走2步,slow一次走一步,如果该链表有环,两个指针必然在环内相遇 //此时只需要把其中的一个指针重新指向链表头部,另一个不变(还在环内), //这次两个指针一次走一步,相遇的地方就是入口节点. //这个定理可以自己去网上看看证明. 1 /* 2 public class ListNode {

数据结构和算法设计专题之---判断单链表中是否有环,环的长度,环的入口节点

题目: 给定一个单链表,只给出头指针head: 1.如何判断是否存在环? 2.如何知道环的长度? 3.如何找出环的连接点在哪里? 4.带环链表的长度是多少? 解法: 1.对于问题1,使用追赶的方法,设定两个指针slow.fast,从头指针开始,每次分别前进1步.2步.如存在环,则两者相遇:如不存在环,fast遇到NULL退出. 2.对于问题2,记录下问题1的碰撞点p,slow.fast从该点开始,再次碰撞所走过的操作数就是环的长度s. 3.问题3:有定理:碰撞点p到连接点的距离=头指针到连接点的

拓扑排序,判断有向图中是否有环

[原创] 今天我们来聊聊有向图中环的判断,在数据结构中我们知道,通过拓扑排序可以判断有向图中是否存在环,对于有向图的存储我们采用邻接表的形势,这里为了简化链表的操作,我们省略了链表,避免了指针的麻烦,直接采用了c++中的vector来模拟链表,操作更加的方便:具体详细的使用,建议百度一下,这里不多说,至于拓扑排序的具体思想,相信大家应该都了解,那么直接上代码,如果有不理解的,建议查阅数据结构书籍,搞懂思想,结合这份代码,很好理解 1 #include <stdio.h> 2 #include

java 链表中是否有环

public class 链表中是否有环{    class ListNode    {        ListNode next;        int val; ListNode(int x)        {            this.val = x;            this.next = null;        }    } public boolean hasCycle(ListNode head)    {        if (head == null)      

判断图中是否有环的方法

一.无向图 方法一:n算法: 原理是: 如果存在回路,则必存在一个子图,是一个环路.环路中所有顶点的度>=2. 第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一. 第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一. 如果最后还有未删除顶点,则存在环,否则没有环. 方法2: DFS搜索图,图中的边只可能是树边或反向边,一旦发现反向边,则表明存在环.该算法的复杂度为O(V). 二.有向图 主要有深度优先和拓扑排序两种方法 判断图中是否有环的方法

判断单向链表中是否有环和查找环的入口

快慢指针 算法描述 定义两个指针slow, fast.slow指针一次走1个结点,fast指针一次走2个结点.如果链表中有环,那么慢指针一定会再某一个时刻追上快指针(slow == fast).如果没有环,则快指针会第一个走到NULL. 实现 结点定义如下: class Node { public Node next; public Object data; public static int sequence = 0; } 算法: /** * 快慢指针 * @param head * @ret

判断单链表中是否有环(循环链表)

有环的定义:链表的尾结点指向了链表中的某个结点,如下图所示 判断是否有环,两种方法: 方法1:使用p.q两个指针,p总是向前走,但q每次都从头开始走,对于每个节点看p走的步数和q是否一样,如上图所示:当p从6走到3时,共走了6步,此时若q从出发,则q只需要走两步就到达3的位置,因而步数不相等,出现矛盾,存在环. 方法2:快慢指针,定义p.q两个指针,p指针每次向前走一步,q每次向前走两步,若在某个时刻出现 p == q,则存在环. 具体代码实现: 1 #include<stdio.h> 2 #