持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
LeetCode 75 —— 142. 环形链表 II
一、题目描述:
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。 示例 2:
输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。 示例 3:
输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。
提示:
链表中节点的数目范围在范围 [0, 104] 内 -105 <= Node.val <= 105 pos 的值为 -1 或者链表中的一个有效索引
进阶:你是否可以使用 O(1) 空间解决此题?
来源:力扣(LeetCode) 链接:leetcode.cn/problems/li… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、思路分析:
-
这道题考察了什么思想?你的思路是什么?
看到这道题,我的第一想法就是用一个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 }是啊,这样果然通过不了所有测试点:
但是,节点的内存地址是独一无二的啊!
我们来试一下:
/** * 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 }果然通过了!!!!
-
做题的时候是不是一次通过的,遇到了什么问题,需要注意什么细节?
不是一次通过的,刚开始没考虑到链表中的值可能会出现重复的,导致部分测试点通不过。
需要将节点地址作为map的键。
-
有几种解法,哪种解法时间复杂度最低,哪种解法空间复杂度最低,最优解法是什么?其他人的题解是什么,谁的效率更好一些?用不同语言实现的话,哪个语言速度最快?
还有一种快慢指针法,我们上一篇求链表的中间节点也用到了这个方法,我们来看一下行不行吧!
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)。
模板来源:
作者:掘金酱
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。