leetcode-141

date
slug
leetcode-141
status
Published
tags
Leetcode
summary
type
Post

龟兔赛跑算法(Floyd 判圈算法)

假想有 乌龟 和 兔子 在链表上移动,兔子跑得快,乌龟跑得慢
如果乌龟和兔子均从链表上的同一个节点开始运动
当该链表有环时,兔子由于跑的快,会先进入环内开始循环,乌龟后进入环内
此时,总有一时刻,兔子会与乌龟相遇
如果该链表无环,兔子会先走出该链表

题目

给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/linked-list-cycle 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析

上面介绍了龟兔赛跑算法,这里我们就来实际使用一下
我们定义两个指针,分别是快指针 fast 和慢指针 slow 作为 兔子 和 乌龟
让 slow 指向链表头部, fast 指向头部的下一节点(为什么?)
当 slow 不等于 fast 时
让 slow 指针前进一个节点(乌龟跑得慢)
fast 指针前进两个节点(兔子跑得快)
如果有一刻,slow 等于 fast 则链表有环
否则无环
下面实现代码

题解

/** * Definition for singly-linked list. * class ListNode { *     int val; *     ListNode next; *     ListNode(int x) { *         val = x; *         next = null; *     } * } */public class Solution {    public boolean hasCycle(ListNode head) {        if(head == null || head.next == null) {            return false;        }        ListNode slow = head;        ListNode fast = head.next;        while(fast != slow) {            if(fast == null || fast.next == null) {                return false;            }            slow = slow.next;            fast = fast.next.next;        }        return true;    }}
由于我们 while 循环的条件是 slow != fast 故我们让 fast 为 head 的下一节点,否则循环不会进入
这个题解就是使用了 O(1) 的内存空间
下面我们看一个使用 O(n) n 表示链表节点总数的方法
我们使用一个 hash 表来存储已访问过的节点
每次取到新节点时,判断节点是否存在于表中
如在,则链表有环
否则无环
public class Solution {    public boolean hasCycle(ListNode head) {        if(head == null || head.next == null) {            return false;        }        var nodes = new HashMap<ListNode, Integer>();        for(ListNode p = head; p != null; p = p.next) {            if(nodes.containsKey(p)) {                return true;            }            nodes.put(p, 1);        }        return false;    }}
相比上一算法,这个方法比较粗暴,不具有优越性

© AlotOfBlahaj 2022 - 2025