链表 - 环形链表I
这里我们以力扣141题为例,讲讲如何判断一个链表是否是环形的。
从图中我们可以非常简单看出,最后一个节点的
Next属性是指向前面的节点的。
那这道题怎么做呢?我们可以假设这样一个场景:🐭和🐱在一个环里面跑,🐭每秒2步,🐱每秒1步,他们的每步长度都是一样的。问:🐭和🐱最终会相遇吗?
答案肯定是会的。由此我们就可以推出:只要二者是在一个环中,二者一定会相遇。
那我们的解题思路也是这样的,定义快慢指针,一个速度快,一个速度慢。
那问题来了,快慢指针的速度怎么定义?回到咱们假设的场景,🐭和🐱的速度需要注意吗?只要是在一个环中,不论他们的速度如何,最终一定会相遇的,这是一定的。所以在咱们的这道题中,快慢指针的速度也任意。但是,由于咱们肯定是希望执行的时间越短越好,所以,咱们定义的快指针速度是2,慢指针的速度是1。速度一前一后,尽量保证不会出现快指针越过慢指针的情况。
不知道大家能不能理解这个图。红色是慢指针走的路程,绿色是快指针走的路程。最开始
slow和fast都是从虚拟头节点开始,每次fast走两步,slow走1步。当快慢指针走了3次后,fast和slow相遇,表示当前链表就是环形的。
上述讲的是循环体中的内容,那现在的循环条件是什么呢?我们可以一步一步推,慢指针不用考虑,因为他一直走在快指针后面,所以重点还是快指针。
快指针的速度是2,换算到代码中就是fast = fast.Next.Next对吧。一个Next是一步。所以这就出来了,fast.Next.Next != nil。那这个条件要满足,是不是fast.Next != nil,那这个条件要满足,是不是fast != nil要满足。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func hasCycle(head *ListNode) bool {
// 虚拟头节点
dummy := &ListNode{
Next: head,
}
slow, fast := dummy, dummy
for fast != nil && fast.Next != nil && fast.Next.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if fast == slow {
return true
}
}
return false
}