LC215 · 数组中的第 K 个最大元素
题意白话解释:给你一堆数字,不要求排序整个数组,只要找出按从大到小排序后排在第 k 位的那个数。
主线提示:只保留当前见过的最大 k 个数。
核心直觉
为什么只留 k 个
如果候选区已经有 k 个比某个数更大的数字,这个数就不可能是第 k 大。
为什么用小顶堆
小顶堆能立刻拿到候选区里最小的数,也就是最该被淘汰的数。
堆顶代表什么
候选区保存最大的 k 个数时,堆顶是这 k 个数中最小的,所以它就是当前第 k 大候选。
动画实验台
读到 3,候选区 [3],未凑够 k 个。
没有超过容量 k,不需要踢掉任何数字;3 暂时只是候选之一。
堆中始终保留“目前已经看过的数字里最大的 k 个”。堆顶是这 k 个数里最小的,所以它是当前第 k 大候选。
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 大。
面试怎么说
我会用大小为 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)。