141 难度 : easy
个人主要思路是, 循环遍历每个节点, 判断该节点此前是否被访问过。
方法一: 时间8ms , 内存 6.8M ,
func hasCycle(head *ListNode) bool {
var val = map[*ListNode]*ListNode{}
if head == nil {
return false
}
val[head] = head
for head.Next != nil {
if _, ok := val[head.Next]; ok {
return true
}else{
val[head.Next] = head.Next
}
head = head.Next
}
return false
}
看了题解之后, 有提升空间
方法二: 快慢指针
使用两个指针, 进行遍历,
slow 每次步长1个节点, fast 每次步长2个节点
func hasCycle(head *ListNode) bool {
slow, fast := head, head
for fast != nil && fast.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if fast == slow {
return true
}
}
return false
}
使用快慢指针可以快执行时间和内存消耗都有很大提升
快慢指针参考: 添加链接描述
142: 难度 :medium
方法一: 用141 的方法一也是可以解决的, 于是做 了处理
func detectCycle(head *ListNode) *ListNode {
var val = map[*ListNode]*ListNode{}
if head == nil || head.Next == nil{
return nil
}
val[head] = head
for head.Next != nil {
if _, ok := val[head.Next]; ok {
return val[head.Next]
}else{
val[head.Next] = head.Next
}
head = head.Next
}
return nil
}
方法二: 题解里的快慢指针(这边直接参考官方的题解), 主要是推导出一个公式
主要根据图示, 设链表中环外部分的长度为 a。show 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast 指针已经走完了环的 n 圈,因此它走过的总距离为 a+n(b+c)+b=a+(n+1)b+nc.此时的slow 比fast 少走一圈
fast 走的路程:a+(n+1)b+nc
slow 走的路程:a+b
根据题意,任意时刻,fast 指针走过的距离都为show 指针的 2 倍。因此,我们有
a+(n+1)b+nc=2(a+b) ⟹ a=c+(n−1)(b+c)
我们会发现:从相遇点到入环点的距离加上 n-1 圈的环长,恰好等于从链表头部到入环点的距离。
所以在slow 与fast 相遇的时候, 再引入一个指针ptr , ptr 走距离a 时, slow 会走剩下的c+(n−1)(b+c), slow 先走c 走进入环点, 剩下的(n-1)圈的环长, 并到达入环点, 此时ptr 也在入环点, 然后就能锁定入环点了。
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
}
跟方法一比较, 消耗了时间, 但是内存消耗减少了