D

当前:LC215 · 数组中的第 K 个最大元素 · 首次出现于 Day 14 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

原题区主解法:大小为 k 的小顶堆

LC215 · 数组中的第 K 个最大元素

题意白话解释:给你一堆数字,不要求排序整个数组,只要找出按从大到小排序后排在第 k 位的那个数。

示例 nums=[3,2,1,5,6,4], k=2,答案 5
第 k 大按排序位置算,不是第 k 个不同元素

主线提示:只保留当前见过的最大 k 个数。

heap main
时间复杂度 O(n log k)
空间复杂度 O(k)
Quickselect 不放进主线动画,最后作为面试进阶单独说明。
intuition

核心直觉

为什么只留 k 个

如果候选区已经有 k 个比某个数更大的数字,这个数就不可能是第 k 大。

为什么用小顶堆

小顶堆能立刻拿到候选区里最小的数,也就是最该被淘汰的数。

堆顶代表什么

候选区保存最大的 k 个数时,堆顶是这 k 个数中最小的,所以它就是当前第 k 大候选。

animation

动画实验台

Step 1 · 读到 3
进度 14%
输入传送带 nums
30
21
12
53
64
45
从左到右读数字;当前读到的数字会先进入候选区。
容量为 k 的候选区 / 小顶堆
k = 2
堆顶3
slot 2-
当前候选区:[3]
当前第 k 大候选答案
heap top
未凑够 k 个
当前发生了什么

读到 3,候选区 [3],未凑够 k 个。

为什么可以踢掉这个数

没有超过容量 k,不需要踢掉任何数字;3 暂时只是候选之一。

当前不变量

堆中始终保留“目前已经看过的数字里最大的 k 个”。堆顶是这 k 个数里最小的,所以它是当前第 k 大候选。

solution

Go 代码

1import "container/heap"23type MinHeap []int45func (h MinHeap) Len() int { return len(h) }6func (h MinHeap) Less(i, j int) bool { return h[i] < h[j] }7func (h MinHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }89func (h *MinHeap) Push(x any) {10    *h = append(*h, x.(int))11}1213func (h *MinHeap) Pop() any {14    old := *h15    x := old[len(old)-1]16    *h = old[:len(old)-1]17    return x18}1920func findKthLargest(nums []int, k int) int {21    h := &MinHeap{}22    heap.Init(h)2324    for _, num := range nums {25        heap.Push(h, num)26        if h.Len() > k {27            heap.Pop(h)28        }29    }3031    return (*h)[0]32}

MinHeap 是什么

MinHeap 是一个 int 切片,再实现 Len、Less、Swap、Push、Pop 这 5 个方法,就能交给 Go 的 container/heap 使用。

Less 为什么用 <

Less 返回 h[i] < h[j],表示较小的数字优先级更高,所以堆顶始终是最小值。这里需要小顶堆,不是最大堆。

为什么 Len > k 就 Pop

每来一个数都先放进候选区;一旦超过 k 个,就弹出堆顶最小值,只留下目前最大的 k 个候选。

为什么最后返回 heap[0]

遍历结束后,堆里正好是最大的 k 个数;heap[0] 是这 k 个数中最小的那个,也就是第 k 大。

interview

面试怎么说

默认回答

我会用大小为 k 的小顶堆,遍历数组,把每个数放入堆中;如果堆大小超过 k,就弹出堆顶最小值。这样堆中始终保留当前最大的 k 个数。最终堆顶就是第 k 大。时间 O(n log k),空间 O(k)。

进阶回答

如果追求平均 O(n),可以用 Quickselect。第 k 大对应升序数组下标 n-k,通过 partition 每次只搜索目标所在的一边。平均 O(n),最坏 O(n²)。

Quickselect 进阶解法显示:平均 O(n),最坏 O(n²),空间 O(1)。

pitfalls

常见误区

第 k 大不是第 k 个不同的数字
小顶堆不是最大堆
堆里不是保存所有数字,只保存 k 个候选
Quickselect 的 O(n) 是平均复杂度,不是最坏复杂度