链表 - 环形链表I

83 阅读2分钟

链表 - 环形链表I

这里我们以力扣141题为例,讲讲如何判断一个链表是否是环形的。

image.png 从图中我们可以非常简单看出,最后一个节点的Next属性是指向前面的节点的。

那这道题怎么做呢?我们可以假设这样一个场景:🐭和🐱在一个环里面跑,🐭每秒2步,🐱每秒1步,他们的每步长度都是一样的。问:🐭和🐱最终会相遇吗?

答案肯定是会的。由此我们就可以推出:只要二者是在一个环中,二者一定会相遇。

那我们的解题思路也是这样的,定义快慢指针,一个速度快,一个速度慢。

那问题来了,快慢指针的速度怎么定义?回到咱们假设的场景,🐭和🐱的速度需要注意吗?只要是在一个环中,不论他们的速度如何,最终一定会相遇的,这是一定的。所以在咱们的这道题中,快慢指针的速度也任意。但是,由于咱们肯定是希望执行的时间越短越好,所以,咱们定义的快指针速度是2,慢指针的速度是1。速度一前一后,尽量保证不会出现快指针越过慢指针的情况。

image.png 不知道大家能不能理解这个图。红色是慢指针走的路程,绿色是快指针走的路程。最开始slowfast都是从虚拟头节点开始,每次fast走两步,slow走1步。当快慢指针走了3次后,fastslow相遇,表示当前链表就是环形的。

上述讲的是循环体中的内容,那现在的循环条件是什么呢?我们可以一步一步推,慢指针不用考虑,因为他一直走在快指针后面,所以重点还是快指针。

快指针的速度是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
}