LeetCode 75 —— 142. 环形链表 II

44 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情

LeetCode 75 —— 142. 环形链表 II

一、题目描述:

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

image-20221004085100498

输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。 示例 2:

image-20221004085108034

输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。 示例 3:

image-20221004085114392

输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。

提示:

链表中节点的数目范围在范围 [0, 104] 内 -105 <= Node.val <= 105 pos 的值为 -1 或者链表中的一个有效索引

进阶:你是否可以使用 O(1) 空间解决此题?

来源:力扣(LeetCode) 链接:leetcode.cn/problems/li… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、思路分析:

  1. 这道题考察了什么思想?你的思路是什么?

    看到这道题,我的第一想法就是用一个map,将所有节点的值存储,如果出现有值再次命中,就说明链表中存在环,当然,这种情况仅限于链表中所有的值都是独一无二的。

    /**
     * Definition for singly-linked list.
     * type ListNode struct {
     *     Val int
     *     Next *ListNode
     * }
     */
    func detectCycle(head *ListNode) *ListNode {
        valMap := map[int]struct{}{}
    ​
        for head != nil{
            if _,ok := valMap[head.Val]; ok{
                return head
            }
            valMap[head.Val] = struct{}{}
            head = head.Next
        }
    ​
        return nil
    }
    

    是啊,这样果然通过不了所有测试点:

    image-20221004090019825

    但是,节点的内存地址是独一无二的啊!

    我们来试一下:

    /**
     * Definition for singly-linked list.
     * type ListNode struct {
     *     Val int
     *     Next *ListNode
     * }
     */
    func detectCycle(head *ListNode) *ListNode {
        valMap := map[*ListNode]struct{}{}
    ​
        for head != nil{
            if _,ok := valMap[head]; ok{
                return head
            }
            valMap[head] = struct{}{}
            head = head.Next
        }
    ​
        return nil
    }
    

    image-20221004090152557

    果然通过了!!!!

  2. 做题的时候是不是一次通过的,遇到了什么问题,需要注意什么细节?

    不是一次通过的,刚开始没考虑到链表中的值可能会出现重复的,导致部分测试点通不过。

    需要将节点地址作为map的键。

  3. 有几种解法,哪种解法时间复杂度最低,哪种解法空间复杂度最低,最优解法是什么?其他人的题解是什么,谁的效率更好一些?用不同语言实现的话,哪个语言速度最快?

    还有一种快慢指针法,我们上一篇求链表的中间节点也用到了这个方法,我们来看一下行不行吧!

    image-20221004090710923

    func detectCycle(head *ListNode) *ListNode {
        slow, fast := head, head
        for fast != nil {
            slow = slow.Next
            if fast.Next == nil {
                return nil
            }
            fast = fast.Next.Next
            if fast == slow {
                p := head
                for p != slow {
                    p = p.Next
                    slow = slow.Next
                }
                return p
            }
        }
        return nil
    }
    ​
    作者:LeetCode-Solution
    链接:https://leetcode.cn/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

三、AC 代码:

func detectCycle(head *ListNode) *ListNode {
    valMap := map[*ListNode]struct{}{}
​
    for head != nil{
        if _,ok := valMap[head]; ok{
            return head
        }
        valMap[head] = struct{}{}
        head = head.Next
    }
​
    return nil
}

四、总结:

快慢指针法计算出了a和c的关系,让空间复杂度降到了O(1),这给我们以启示:有时候将复杂的问题用数学建模分析可能会收获意想不到的结果!

哈希表的时间复杂度和空间复杂度都为O(N),而快慢指针法的时间复杂度为O(N),空间复杂度为O(1)。

模板来源:

作者:掘金酱

链接:juejin.cn/post/706970…

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。