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

哈希表法

  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 小时前
leecodecode【面试150】【2026.6.14打卡-java版本】
java·算法·面试
JustHappy7 小时前
古法编程秘籍(七):互联网到底是什么?把两台电脑怎么说话搞懂就够了
前端·后端·网络协议
Hommy887 小时前
【剪映小助手】添加图片接口(Add Images)
后端·github·剪映小助手·视频剪辑自动化
GetcharZp8 小时前
别再盲目用 OpenCV 读图了,这才是 CV 预处理的终极杀手锏!
后端
kyriewen8 小时前
Git Commit 前自动修复代码风格?配置 Husky + lint-staged,从此 CR 只聊逻辑
前端·git·面试
程序员二叉8 小时前
【JUC】ThreadLocal底层原理|内存泄漏|弱引用|跨线程传递方案
java·开发语言·面试·职场和发展·juc
程序员二叉8 小时前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
IT_陈寒12 小时前
Vite热更新失效?可能你在用Windows
前端·人工智能·后端
IT龟苓膏12 小时前
并发深度解析】硬核手撕 ForkJoinPool + WorkStealing + CompletableFuture 底层源码与大厂面试演练
面试·职场和发展
椰椰椰耶13 小时前
[SpringCloud][14]OpenFeign参数传递方法
后端·spring·spring cloud