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

快慢指针

算法描写叙述

定义两个指针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;
    }

原文地址:https://www.cnblogs.com/llguanli/p/8452083.html

时间: 2024-10-16 01:33:57

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

【华为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

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

快慢指针 算法描述 定义两个指针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

在链表中获取一个数据、查找操作C语言实现

SN *Get_S_Node ( SN *head ) { /* head 为要查询的链表的头指针 */ SN *Get_S_Node = NULL; INT32 OSM = 1,i32i = 0, data_num = 0; /* OSM是标志符,i32i是一个循环体内的变量,data为要获取的元素的序号 */ Get_S_Node = ( SN * )malloc( sizeof (SN) ); Get_S_Node = head; /* 输入要获取的数据 */ OSM = OSM_Prin

第九十题(1.不开辟暂时空间交换 2.删除串中指定字符 3.推断链表中存在环)

1.不开辟用于交换数据的暂时空间,怎样完毕字符串的逆序 2.删除串中指定的字符 3.推断单链表中是否存在环 分析和代码: 1,不开辟用于交换的暂时空间,能够用异或交换.或者用字符串的'\0'位置的空间(打个擦边球,使用已有空间.不算开辟). void switch1(char* str) //使用异或交换 { int len = strlen(str); for (int i = 0; i < len / 2; i++) str[i] ^= str[len - i - 1] ^= str[i]

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

问题:如何判断一个单向链表中是否存在环? 例如: 链表中存在环(B-->D): <-- <--^ | | v | A-->B-->C-->D 链表中不存在环: A-->B-->C-->D-->E-->F 解题思路: ??从一个实际的生活场景出发,两个人,在一个环形的操场上跑步的时候,如果有一个人跑得比另一个人还要快,那么,在n圈之后,这两个人总会在操场上的某个点相遇.将操场类比于链表中存在的环路径,将两个人看成两个指针,那么这道题的解题思路