检测链表是否有环, 动画演示, Floyd判圈算法扩展应用

力扣原题链接: 141. 环形链表 - 力扣(LeetCode)

哈希表

检测环形链表, 直观的思路就是使用哈希表, 遍历这个链表, 将访问过的节点加入到哈希表中, 如果遍历过程中发现节点已经存在于哈希表中, 则说明链表有环.

复杂度分析:

  • 时间复杂度: O(N), 最坏情况下需要遍历所有节点.
  • 空间复杂度: O(N), 最坏情况下每个节点都会加入到哈希表.

Floyd判圈算法

另一种更高效的算法是Floyd判圈算法, 又称龟兔赛跑算法, 该算法由计算机科学家罗伯特·弗洛伊德(Robert W. Floyd)提出, 其核心思想是通过两个指针以不同速度遍历链表来判断是否存在环, 并可以进一步确定环的起点和长度.

算法步骤

初始化两个指针:

  • 慢指针: 每次移动一步
  • 快指针: 每次移动两步

两个指针同时从起点出发, 按照各自的速度移动.

判断是否有环:

  • 如果快指针会先到达终点, 则说明链表无环
  • 如果快慢指针相遇, 则说明链表有环

 

动画演示

欢迎关注公众号: 算法铁金库

持续更新数据结构与算法, 动画演示, 理解算法快人一步~

Java代码:

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) {
            return false;
        }

        ListNode slow = head;
        ListNode fast = head;
        do {
            slow = slow.next;
            if (fast.next != null && fast.next.next != null) {
                fast = fast.next.next;
            } else {
                return false;
            }
        } while (slow != fast);
        return true;
    }
}

 

复杂度分析:

  • 时间复杂度: O(N), 慢指针对每个节点至多访问一次, 快指针访问的节点个数不超过慢指针的2倍.
  • 空间复杂度: O(1), 只需要两个额外指针.

 

思考: 为什么当链表有环, 快慢指针一定能相遇?

如果存在环, 快指针每次比慢指针多走一步, 也就是说距离每次减1, 所以最终一定能相遇.

扩展应用

确定环的起点

当快慢指针首次相遇后, 将其中一个指针移回起点(如慢指针), 然后两个指针改为每次均移动一步. 当它们再次相遇时, 相遇点即为环的起点.

 

证明:

用a表示链表头到环起点的距离

用L表示环的长度

第一阶段: 寻找相遇点

  • 当慢指针走了a步到达环起点时, 快指针已经走了2a步
  • 快指针在环中的位置是 (2a - a) % L = a % L (因为前a步用来走到环起点)
  • 此时快指针需要追赶慢指针, 相对距离是 L - a % L
  • 由于快指针每次比慢指针多走1步, 经过 L - a % L 步后两者相遇
  • L - a % L 就是慢指针在环中移动的长度, 也就是相遇位置距环起点的距离

第二阶段: 确定环起点

  • 将慢指针移回链表头部, 快指针不动, 接下来两个指针每次各走1步
  • 慢指针走了a步到达环起点, 同时快指针也走了a步, 在环中的位置为 L - a % L + a
  • 而 (L - a % L + a) % L = 0, 也就是说该位置就是环起点.

证毕.

计算环的长度

在确定有环后, 固定一个指针, 另一个指针继续移动并计数, 直到再次相遇, 计数结果即为环的长度.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值