如何判断一个链表是否有环?以及提供入口节点解决方法

哈希表法

  1. 算法思路:遍历链表,使用一个哈希表来记录已经访问过的节点。对于链表中的每个节点,检查该节点是否已经在哈希表中。如果在哈希表中,说明链表存在环;如果遍历到链表的末尾(即节点为null)都没有在哈希表中发现重复节点,说明链表无环。

  2. 代码实现

    ini 复制代码
    class ListNode {
            int val;
            ListNode next;
            ListNode(int x) {
                val = x;
                next = null;
            }
    }
    
     //哈希表法处理链表是否有环
        public boolean hasCycleHash(ListNode head) {
            HashSet<ListNode> visited = new HashSet<ListNode>();
            ListNode current = head;
            while (current != null) {
                // 如果当前节点已经在哈希表中,说明有环
                if (visited.contains(current)) {
                    return true;
                }
                // 将当前节点添加到哈希表中
                visited.add(current);
                current = current.next;
            }
            return false; // 遍历完链表没有发现环
        }
    
    public static void main(String[] args) {
            //测试无环场景
            ListNode nodeA = solution.new ListNode(1);
            ListNode nodeB = solution.new ListNode(2);
            ListNode nodeC = solution.new ListNode(3);
            ListNode nodeD = solution.new ListNode(4);
            ListNode nodeE = solution.new ListNode(5);
    
            nodeA.next = nodeB;
            nodeB.next = nodeC;
            nodeC.next = nodeD;
            nodeD.next = nodeE; // 无环
            boolean resultNoCycle = solution.hasCycleHash(nodeA);
            System.out.println("哈希法处理:链表是否有环: " + resultNoCycle); // 输出: 链表是否有环: false
    }

结果展示

arduino 复制代码
哈希法处理:链表是否有环: false

快慢指针法

  1. 算法思路:设置两个指针,一个慢指针(slow)和一个快指针(fast)。慢指针每次移动一个节点,快指针每次移动两个节点。如果链表没有环,快指针会先到达链表末尾;如果链表有环,快指针会在环内追上慢指针,即快慢指针会相遇,此时说明链表存在环。

  2. 代码实现

    ini 复制代码
        //创建节点信息
    	class ListNode {
    			        int val;
    			        ListNode next;
    			        ListNode(int x) {
    			            val = x;
    			            next = null;
    			        }
    	}
    //快慢法
    public boolean hasCycle(ListNode head) {
            if (head == null || head.next == null) {
                return false; //  链表为空或只有一个节点,不可能有环
            }
            // 使用快慢指针法检测环
            ListNode slow = head;
            ListNode fast = head;
            while (fast != null && fast.next != null) {
                // 慢指针走一步,快指针走两步
                slow = slow.next;
                fast = fast.next.next;
                // 快慢指针相遇,说明有环
                if (slow == fast) {
                    return true;
                }
            }
            return false; // 没有环
        }
    //测试
    public static void main(String[] args) {
            LinkedListCycle solution = new LinkedListCycle();
            ListNode node1 = solution.new ListNode(3);
            ListNode node2 = solution.new ListNode(2);
            ListNode node3 = solution.new ListNode(0);
            ListNode node4 = solution.new ListNode(-4);
            node1.next = node2;
            node2.next = node3;
            node3.next = node4;
            node4.next = node2; // 创建环
    
        // 测试链表是否有环
        //测试无环场景
    		node4.next = null ; // 无环
        boolean result = solution.hasCycle(node1);
        System.out.println("链表是否有环: " + result); // 输出: 链表是否有环: true
        //测试有环场景
        ListNode entryNode = solution.detectCycle(node1);
        if (entryNode != null) {
            System.out.println("环的入口节点值: " + entryNode.val); // 输出: 环的入口节点值: 2
        } else {
            System.out.println("链表无环");
        }
    }

结果展示

makefile 复制代码
链表是否有环: true
环的入口节点值: 2

寻找环的入口

代码实现:

ini 复制代码
//找到环的入口节点
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) {
            return null; // 链表为空或只有一个节点,不可能有环
        }
        // 初始化快慢指针
        ListNode slow = head;
        ListNode fast = head;
        boolean hasCycle = false;
        // 使用快慢指针法检测环
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;

        if (slow == fast) {
            hasCycle = true; // 快慢指针相遇,说明有环
            break;
        }
    }
    if (!hasCycle) {
        return null; // 没有环
    }
    // 找到环的入口节点
    //p1从头开始,p2从相遇点开始
    ListNode p1, p2;
    p1 = head;
    p2 = slow;
    while (p1 != p2) {
        p1 = p1.next;
        p2 = p2.next;
    }
    return p1; // 返回环的入口节点
}
相关推荐
自由的疯1 小时前
Java RuoYi整合Magic-Api详解
java·后端·架构
自由的疯1 小时前
Java 实现TXT文件上传并解析的Spring Boot应用
后端·架构
开始学java2 小时前
抽象类和抽象方法
后端
华仔啊2 小时前
别再乱 new ArrayList!8 大 Java 容器选型案例,一篇看懂
java·后端
小码编匠2 小时前
手把手教会设计 WinForm 高DPI兼容程序,告别字体模糊与控件乱飞(.NET 4.6.1/.NET 6.0)
后端·c#·.net
秋难降3 小时前
MySQL 优化:告别 “996”,让系统高效运行
数据库·后端·mysql
追逐时光者3 小时前
很强!一款基于 .NET 构建、功能强大、通用的 2D 图形编辑器
后端·.net
mzlogin4 小时前
Java|FreeMarker 复用 layout
java·后端·freemarker
MrHuang9654 小时前
【创建线程的四种方式】
后端
双向334 小时前
Python 多线程日志错乱:logging.Handler 的并发问题
后端