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

快慢指针

算法描述

定义两个指针slow, fast。slow指针一次走1个结点,fast指针一次走2个结点。如果链表中有环,那么慢指针一定会再某一个时刻追上快指针(slow == fast)。如果没有环,则快指针会第一个走到NULL。

实现

结点定义如下:

class Node {
    public Node next;
    public Object data;

    public static int sequence = 0;
}

算法:

/**
     * 快慢指针
     * @param head
     * @return
     */
    public static boolean checkCircle(Node head) {
        Node fast = null;
        Node slow = null;

        fast = head;
        slow = head;
        while (true) {
            // 慢指针移动一步
            if (null != slow.next) {
                slow = slow.next;
            } else {
                return false;
            }

            // 快指针移动两步
            if (null != fast.next && null != fast.next.next) {
                fast = fast.next.next;
            } else {
                return false;
            }

            // 检查是否相遇
            if (slow == fast) {
                return true;
            }
        }
    }

步数检查

算法描述

上面的算法只能判断链表中有没有环,如果我们想找出环的入口怎么办呢?

定义两个指针p, q。p每走一个结点(即一步),q则从头一直向后走,直到q走到NULL或p, q走到同一个结点但走过的步数不相同为止。此时q的步数就是环入口在结点中的位置。如果走到NULL则说明链表不存在环。

为什么p, q走到同一个结点但步数不相等时就说明有环呢?因为如果p, q步数相同,说明它们走过的结点是一样的,如果p, q步数不同了,则说明p是从环里走了一圈又回到了环的入口,此时q到达该结点时还没有走过环,因此步数不相等,而且此时q的步数就是环的入口。

实现

/**
     * 查找环的起点
     * @param head
     * @return 返回元素的索引,从0开始。没有找到返回-1
     */
    public static int findCircleEntry(Node head) {
        Node p = head; // 总是从头开始
        Node q = head;

        int pSteps = 0;
        int qSteps = 0;
        while (null != q.next) {
            q = q.next;
            ++qSteps;

            // p从头开始走
            while (null != p.next) {
                p = p.next;
                ++pSteps;

                // 当p与q指向同一个结点时
                if (p == q) {
                    // 如果走的步数不同,则这就是入口
                    if (pSteps != qSteps) {
                        return pSteps - 1;
                    } else {
                        // 走的步数相同,不是入口
                        break;
                    }
                }
            }

            p = head; // 回到头结点
            pSteps = 0;
        }

        // 其中有一个指针走到了头,说明没有环
        return -1;
    }

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 18:06:23

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

判断单向链表是否有环,环起点,环长,链表长

今天在微信上看到一篇介绍如何判断单向链表是否有环的文章,感觉很有意思,整理一下读后的思路. 一.判断单向链表是否有环 方法1:设置一个Hashset,顺序读取链表中的节点,判断Hashset中是否有该节点的唯一标识(ID).如果在Hashset中,说明有环:如果不在Hashset中,将节点的ID存入Hashset. 这种方法时间复杂度已经最优,但是因为额外申请了Hashset,所以空间复杂度不算最优. 方法2:设置2个指针,指向头节点.第1个指针每次指向下一个节点:第2个指针指向下一个节点的下一

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

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

【华为OJ】【035-输出单向链表中倒数第k个结点】

[华为OJ][算法总篇章] [华为OJ][035-输出单向链表中倒数第k个结点] [工程下载] 题目描述 输入一个单向链表,输出该链表中倒数第k个结点,链表的倒数第0个结点为链表的尾指针. 输入描述 输入说明 1 输入链表结点个数 2 输入链表的值 3 输入k的值 输出描述 输出一个整数 输入例子 8 1 2 3 4 5 6 7 8 4 输出例子 4 算法实现 import org.omg.SendingContext.RunTime; import java.util.List; import

[华为机试练习题]49.向升序单向链表中插入一个节点

题目 描述: 输入一个升序单向链表和一个链表节点,向单向链表中按升序插入这个节点. 输入为空指针的情况视为异常,另外不考虑节点值相等的情况. 链表结点定义如下: struct ListNode { int m_nKey; ListNode* m_pNext; }; 详细描述: 接口说明 原型: ListNode* InsertNodeToList(ListNode* pListHead, ListNode* pInsertNode); 输入参数: ListNode* pListHead 单向链表

找出单向链表中的倒数第k个结点

import java.util.Scanner; public class List { private Node first; private int N; class Node{ int data; Node next; } //顺便复习一下链表 public int size() { return N; } public boolean isEmpty() { return first==null; } public Node FindPrev(int pos){ Node tmp=fi

向升序单向链表中插入一个节点

#include "OJ.h" /* 功能: 输入一个升序单向链表和一个链表节点,向单向链表中按升序插入这个节点. 输入为空指针的情况视为异常,另外不考虑节点值相等的情况. 输入: ListNode* pListHead 单向链表 ListNode* pInsertNode 新插入节点 输出: ListNode* pListHead 单向链表 返回: 正常插入节点返回链表头指针,其它异常返回空指针 */ ListNode* InsertNodeToList(ListNode* pLis

删除单向链表中的某个节点

删除单向链表中的某个节点 链表的节点删除 在删除链表的节点步骤如下: 1.找到被删除节点的前面一个节点 2.将前面节点的next节点改成下一个节点 3.将被删除节点的内存释放 public class deleteNode { Node head; class Node { int data; Node next; Node(int d) { data = d; next = null; } } void deleteNode(int key) { // Store head node Node

堆排序的实现(联发科子公司创发科技笔试)及判断单向链表的环

先上代码: #include <iostream> #include <algorithm> using namespace std; void HeapAdjust(int data[],int i,int length) { int nChild; int nTemp; for(nTemp=data[i]; 2*i+1<length ;i=nChild) { nChild=2*i+1; if(nChild<length-1&&data[nChild+

笔试算法题(27):判断单向链表是否有环并找出环入口节点 &amp; 判断两棵二元树是否相等

出题:判断一个单向链表是否有环,如果有环则找到环入口节点: 分析: 第一个问题:使用快慢指针(fast指针一次走两步,slow指针一次走一步,并判断是否到达NULL,如果fast==slow成立,则说明链表有环): 第二个问题:fast与slow相遇时,slow一定还没有走完一圈(反证法可证明):  示意图A为起始点,B为环入口点,C为相遇点,则a1=|AB|表示起始点到换入口的距离,a2=|CB|表示相遇点到环入口点的距离,s1=|AB|+|BC|表示slow指针走的长度,s2表示fast指针