【数据结构】28、判断链表是否有环

因为最近小米电话面试被问到如何判断一个链表是否有环,那今天正好实现以下这个算法

1.链表

package y2019.Algorithm.LinkedList;

/**
 * @ProjectName: cutter-point
 * @Package: y2019.Algorithm.LinkedList
 * @ClassName: FindRing
 * @Author: xiaof
 * @Description: 现在存在一条链表,寻找这个链表是否存在环
 * @Date: 2019/6/24 9:12
 * @Version: 1.0
 */
public class FindRing {

    public class Node {
        private String key;
        private String value;
        private Node next;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public Node getNext() {
            return next;
        }

        public void setNext(Node next) {
            this.next = next;
        }
    }

    //链表头结点
    private Node ringHead;
    private Node ringTail;
    private int length;

    public FindRing() {
        ringHead = ringTail = null;
    }

    //创建一个长度为n的链表
    public FindRing(int length) {
        ringHead = ringTail = null;
        Node curNode = ringHead;
        for(int i = 0; i < length; ++i) {
            if(ringHead == null) {
                ringHead = new Node();
                ringHead.key = i + " " + System.currentTimeMillis();
                curNode = ringHead;
                ringTail = curNode;
            } else if (curNode.next == null) {
                curNode.next = new Node();
                curNode = curNode.next;
                curNode.key = i + " " + System.currentTimeMillis();
                ringTail = curNode;
            }
        }
        this.length = length;
    }

    public int size() {
        return length;
    }

    public void add(Node newNode) {
        //添加节点进入链表
        //尾部插入
        if(ringTail == null) {
            ringTail = newNode;
            ringTail = ringTail.next;
        } else {
            Node temp = ringTail.next;
            temp = newNode;
            ringTail = temp.next;
        }
        ++length;
    }

    public Node getByIndex(int index) {
        Node resultNode = ringHead;
        for(int i = 1; i < index; ++i) {
            resultNode = resultNode.next;
        }
        return resultNode;
    }

    public Node getRingHead() {
        return ringHead;
    }

    public void setRingHead(Node ringHead) {
        this.ringHead = ringHead;
    }

    public Node getRingTail() {
        return ringTail;
    }

    public void setRingTail(Node ringTail) {
        this.ringTail = ringTail;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }
}

内部类的使用,是为了方便调试

测试类:

package LinkedList;

import org.junit.jupiter.api.Test;
import y2019.Algorithm.LinkedList.FindRing;

import java.util.HashSet;
import java.util.Set;

/**
 * @ProjectName: cutter-point
 * @Package: LinkedList
 * @ClassName: Test1
 * @Author: xiaof
 * @Description: ${description}
 * @Date: 2019/6/24 9:31
 * @Version: 1.0
 */
public class Test1 {

    @Test
    public void test1 () {
        //判断一个链表是否有环
        //1.实现链表set方式,也就是通过hashcode进行定位
        FindRing findRing = new FindRing(15);

        //创建环节点
        int ringIndex = (int) (Math.random() * 10);
        ringIndex = 5;
        FindRing.Node ringNode = findRing.getByIndex(ringIndex);
        FindRing.Node tailNode = findRing.getRingTail();
        tailNode.setNext(ringNode);

        //开始判断是否有环
        Set set = new HashSet();
        //循环遍历链表,直到得到重复的
        FindRing.Node indexNode = findRing.getRingHead();
        int indexCount = 0;
        while(true) {
            if(indexNode == null) {
                System.out.println("无环");
                break;
            }
            if(set.contains(indexNode)) {
                System.out.println("有环,key:" + indexNode.getKey() + " 遍历次数:" + indexCount);
                break;
            }
            set.add(indexNode);
            indexNode = indexNode.getNext();
            indexCount += 1;
        }

        System.out.println("结束遍历");

    }

    @Test
    public void test2 () {
        //判断一个链表是否有环
        //1.通过2个指针同时查询,直到两个指针指向同一个节点作为有环,如果结束,那么无换
        FindRing findRing = new FindRing(15);

        //创建环节点
        int ringIndex = (int) (Math.random() * 10);
        ringIndex = 5;
        FindRing.Node ringNode = findRing.getByIndex(ringIndex);
        FindRing.Node tailNode = findRing.getRingTail();
        tailNode.setNext(ringNode);

        //开始判断是否有环
        FindRing.Node index1 = findRing.getRingHead();
        FindRing.Node index2 = findRing.getRingHead().getNext();
        int indexCount = 0;
        int indexCount2 = 0;
        //第一个每次遍历一个,第二个每次遍历2个
        while (true) {

            if(index1 == null || index2 == null || index2.getNext() == null) {
                System.out.println("无环");
                break;
            }

            if(index1 == index2) {
                System.out.println("有环,key:" + index1.getKey() + " 遍历次数1:" + indexCount + " 遍历次数2:" + indexCount2);
                break;
            }

            indexCount += 1;
            indexCount2 += 2;
            index1 = index1.getNext();
            index2 = index2.getNext().getNext();
        }

        System.out.println("结束遍历");
    }
}

结果:

测试一:

测试二:

从这里我们可以判断到第二个方法并不能定位到产生环的节点是哪个节点,并且循环次数比第一个多

总结:

方式一:对空间占用比较大,但是时间复杂度底,并且可以定位到产生环节点的位置

方式二:对空间占用比较小,但是时间复杂度高,并且无法定位到具体产生环的节点

原文地址:https://www.cnblogs.com/cutter-point/p/11075701.html

时间: 2024-12-14 22:23:06

【数据结构】28、判断链表是否有环的相关文章

判断链表是否有环及两链表是否相交

单向链表是最常用的数据结构之一,因此总结其常见的问题及其解决方案是很有意义的. 问题1:如何判断一个单向链表是否有环?如果有,如何找到其入口节点的指针? 算法思想:用两个指针p1,p2同时指向链表的头部,p1一次移动一步,p2一次移动两步,如果最终p1和p2重合则说明链表有环,如果p2走到空指针(链表的结尾)则说明链表无环: 如果最终p1和p2重合,使p2重新指向链表的头结点,然后p1和p2同时一次移动一步,当p1和p2再次重合时该节点指针就是环的入口节点指针. 算法实现: [cpp] view

链表(13)----判断链表是否有环,并返回环入口节点

1.链表定义 typedef struct ListElement_t_ { void *data; struct ListElement_t_ *next; } ListElement_t; typedef struct List_t_{ int size; int capacity; ListElement_t *head; ListElement_t *tail; } List_t; 2.判断链表是否有环,并返回链表入口节点 (1)方法一:根据相遇节点.头结点.入口节点之间的距离关系求解

判断链表是否有环

Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using extra space? 关键点:1)判断链表是否有环. 2)一个小坑在判断root和root的next是否为空上. 3)可以看为追及问题.最关键的坑在判断快走(每次走2步的节点),走1步会不会已经走到头... /** * Definition for singly-linked list. * str

判断链表是否有环及环入口点的求法(Linked List Cycle II )

分为两步 第一步 还是利用快慢指针,如果有环的话在利用快慢指针终会相会于一个节点. 第二步.然后从这节点出发每次出发走一步,同时从根节点出发每次出发也走一步则他们两个指针相遇的地方就是环的入口. 第一步好解释那么第二步是为什么呢? 网上有很多解法大都是从数学的角度来分析,有公式也有推算很不直观,我从图形的角度来看的话就相对理解起来简单很多. 将图摊开成一条线,假设我们有环而且假设快指针就多走了一圈就与慢指针相遇了(多走n圈其实也是一样的,画出图来也不难理解,只是画起来麻烦索性就以一圈来代表) 红

判断链表是否带环,若带环,找到环的入口点

#pragma once #include<iostream> using namespace std; template<class T> struct LinkNode { T _data; LinkNode* _next; LinkNode(const T& x) :_data(x) , _next(NULL) {} }; template<class T> class ListNode { //为了安全性 private: ListNode(const 

C语言强化(七)链表相交问题_3 判断链表是否带环

前两篇讨论的前提都是链表是无环的,但是如果链表有环呢? 显然,如果链表有环,那么之前的寻找链表尾结点的函数将陷入死循环,之前的算法也将崩掉. 所以对于链表相交的问题,首先要判断的是链表是否有环. 题目 给出俩个单向链表的头指针,比如 h1,h2,判断这俩个链表是否相交 解题步骤 判断两个[无环]链表是否相交 找到两个[无环]链表的相交结点 判断链表是否带环 判断两个[有环]链表是否相交 找到两个[有环]链表的相交结点 思路 用两个指针,一个指针步长为1,一个指针步长为2,若最后相遇,则链表有环

判断链表是否有环及入口点

判断链表是否有环方法--快慢指针 一. 问:如何判断是否有环? 答:如果有两个头结点指针,一个走的快,一个走的慢,那么若干步以后,快的指针总会超过慢的指针一圈. 设置两个指针(fast,slow),初始值都指向头,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定相遇.(当然,fast先行头到尾部为NULL,则为无环链表). #include <stdio.h> typedef struct ListNode { int val

判断链表是否带环,以及环的入口

给出一个链表,先判断链表是否带环,如果带环,求出环的入口. 判断是否带环:用快慢指针.快指针每走两步,慢指针走一步,如果两者在某个点处相 遇,则链表带环. 下边给出函数的实现代码: typedef struct LinkNode { DataType data; struct LinkNode *next; }LinkNode,*pLinkNode; typedef struct LinkList { LinkNode *pHead; }LinkList,*pLinkList; pLinkNod

Floyd判圈算法(判断链表是否含环)

Floyd判圈算法 简介 Floyd判圈算法,也称龟兔赛跑算法,可用于判断链表.迭代函数.有限状态机是否有环.如果有,找出环的起点和大小.时间复杂度O(n),空间复杂度O(1). 可以先思考一下,假设有一个圆形的跑道周长为\(C\),A和B从同一个起点,分别以\(v\)和\(2v\)的速度同向出发,可以知道,因为B比A跑得快而且跑道是环形的,所以接下来一定存在某一时刻,B和A相遇,这时候,B跑过的总距离\(S_B\)减去A跑的总距离\(S_A\)一定是\(C\)的整数倍.即: \(S_B-S_A

链表:判断链表是否带环 、求两个链表的相交结点

问题一:返回两个链表的相交结点1.先分别得到两个链表的长度2.得到长度差,3.先让长链表的头结点走(长度差)步.4.这时.短链表头结点还在原地,两者开始一起走,当得到两者val相等时,这个结点就是公共结点,即相遇结点. 问题二:判断链表是否带环1.定义两个快慢指针,快指针先走两步,慢指针再走一步.直到快慢指针当前结点相同. 如果快指针先为null,则表示没有环,返回null.2.如果带环,让起点和相遇点同时出发.同走一步,再判断相等与否,如果相等退出循坏 返回这个结点 ```public cla