LeetCode刷题笔记 141、142(涉及到链表)

本文介绍两种检测链表是否包含环的有效算法:哈希表和双指针法。哈希表通过存储节点引用实现,适合空间充足场景;双指针法采用快慢指针,仅需常数级额外空间,适用于资源受限环境。文章还详细解析了如何定位环的起始节点。
摘要由CSDN通过智能技术生成

题目:环形链表I
在这里插入图片描述
答案:
参考链接:https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode/

1.哈希表

遍历所有结点并在哈希表中存储每个结点的引用(或内存地址)。如果当前结点为空结点 null(即已检测到链表尾部的下一个结点),那么我们已经遍历完整个链表,并且该链表不是环形链表。如果当前结点的引用已经存在于哈希表中,那么返回 true(即该链表为环形链表)。

public class Solution {
    public boolean hasCycle(ListNode head) {
        Set<ListNode> nodesSeen = new HashSet<>();
        while (head != null) {
            if (nodesSeen.contains(head)) {
                return true;
            } else {
                nodesSeen.add(head);
            }
            head = head.next;
        }
        return false;
    }
}

注:使用HashSet而非HashMap的原因:HashSet可以只有一个参数

时间复杂度:O(n)
空间复杂度:O(n)

2.双指针

通过使用具有不同速度的快、慢两个指针遍历链表。慢指针每次移动一步,而快指针每次移动两步。
如果列表中不存在环,最终快指针将会最先到达尾部,返回 false。
如果存在环,则快指针一定会追上慢指针(如果快指针比慢指针慢一步,在下一次迭代中,分别走两步及一步并相遇;如果快指针比慢指针慢2–3步甚至更多,在之后的迭代中也会变成只慢一步)(每走1轮,fast 与 slow 的间距 +1,fast 终会追上 slow

public boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) {
        return false;
    }
    ListNode slow = head;
    ListNode fast = head.next;
    while (slow != fast) {
        if (fast == null || fast.next == null) {
            return false;
        }
        slow = slow.next;
        fast = fast.next.next;
    }
    return true;
}

时间复杂度:O(n)
空间复杂度:O(1)

题目:环形链表II
在这里插入图片描述
答案:

1.哈希表

用一个 Set 保存已经访问过的节点,我们可以遍历整个列表并返回第一个出现重复的节点

public class Solution {
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> nodesSeen = new HashSet<>();
        while (head != null) {
            if (nodesSeen.contains(head)) {
                return head;
            } else {
                nodesSeen.add(head);
            }
            head = head.next;
        }
        return null;
    }
}

时间复杂度:O(n)
空间复杂度:O(n)

2.双指针

转自:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/linked-list-cycle-ii-kuai-man-zhi-zhen-shuang-zhi-/

算法流程:
1.双指针第一次相遇:设两指针 fast,slow 指向链表头部 head,fast 每轮走2步,slow 每轮走1步;
第一种结果:fast指针走过链表末端,说明链表无环,直接返回null
第二种结果:fast == slow时,两指针第一次相遇。

设链表共有a+b个节点,其中链表头部到链表入口 有a个节点(不计链表入口节点), 链表环 有b个节点(这里需要注意,a和b是未知数);设两指针分别走了f,s步,则有:
(1)fast 走的步数是slow步数的 2倍,即 f = 2s;
(2)fast 比 slow多走了 n个环的长度,即 f = s + nb;( 解析: 双指针都走过 a 步,然后在环内绕圈直到重合,重合时 fast 比 slow 多走环的长度整数倍 )
以上两式相减得:f = 2nb,s = nb,即fast和slow 指针分别走了 2n,n 个 环的周长 (注意: nn 是未知数,不同链表的情况不同)
2.目前情况分析:
所有走到链表入口结点的步数是:k=a+nb(先走a步到入口节点,之后每绕 1 圈环( b 步)都会再次到入口节点)
而目前,slow 指针走过的步数为 nb 步。因此,我们只要想办法让 slow 再走 a 步停下来,就可以到环的入口。
3.双指针第二次相遇
slow指针位置不变 ,将fast指针重新指向链表头部节点 ;slow和fast同时每轮向前走 1 步;
TIPS:此时 f = 0,s = nb ;
当 fast 指针走到f = a时,slow 指针走到s = a+nb,此时两指针重合,并同时指向链表环入口

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head, slow = head;
        while (true) {
            if (fast == null || fast.next == null) return null;
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) break;
        }
        fast = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return fast;
    }
}

时间复杂度:O(n)
空间复杂度:O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值