题目描述

:::fold-green 相关原题

LeetCode 原题: ✅ LCR 159. 库存管理 III

LeetCode 原题选取最小的 k 个数;本文改为选取最大的 k 个数,重复值仍按出现次数保留。

:::

给定整数数组 arr 和整数 k,返回其中最大的 k 个数,结果顺序不限,重复值按出现次数保留。

示例 1:

输入: arr = [3,1,5,2], k = 2
输出: [3,5]

提示:

  • 0 <= k <= arr.length
  • k = 0 时返回空数组。

题意分析

只需要保留最大的 k 次出现,不必给全部元素排序。维护这 k 个候选中的最小值,才能在新数到来时快速判断它是否值得替换当前最弱候选。

解法:大小为 k 的小顶堆

核心思路

[!blue]

堆中始终保留目前见过的最大 k 个数,堆顶必须是其中最小的一个,因为它是最先应该被淘汰的候选。这就是求“最大 k 个数”反而使用小顶堆的原因。

未满 k 个时直接加入;已满时,新值不大于堆顶就不能改善结果,否则删除堆顶并加入新值。每次只改变一个候选,其他保留值仍优于已丢弃的值。

重复值按不同出现保留,不做集合去重。k 为 0 时直接返回空数组,避免读取空堆。

解题步骤

  1. k=0 时直接返回空数组,否则创建小顶堆。
  2. 堆未满 k 个时直接插入当前值。
  3. 堆已满时,只在当前值大于堆顶的情况下删除堆顶并插入当前值。
  4. 遍历结束返回堆中全部元素,输出无需再排序。

代码实现

class Solution {
    public int[] getLargestNumbers(int[] arr, int k) {
        int[] res = new int[k];

        if (k == 0) {
            return res;
        }

        PriorityQueue<Integer> heap = new PriorityQueue<>((a, b) -> Integer.compare(a, b));

        for (int x : arr) {
            if (heap.size() < k) {
                heap.offer(x);
            } else if (x > heap.peek()) {
                heap.poll();
                heap.offer(x);
            }
        }

        for (int i = 0; i < k; i++) {
            res[i] = heap.poll();
        }

        return res;
    }
}
import "container/heap"

type minHeap []int

func (h minHeap) Len() int {
    return len(h)
}

func (h minHeap) Less(i, j int) bool {
    return h[i] < h[j]
}

func (h minHeap) Swap(i, j int) {
    h[i], h[j] = h[j], h[i]
}

func (h *minHeap) Push(x any) {
    *h = append(*h, x.(int))
}

func (h *minHeap) Pop() any {
    old := *h
    n := len(old)
    v := old[n-1]
    *h = old[:n-1]
    return v
}

func getLargestNumbers(arr []int, k int) []int {
    if k == 0 {
        return []int{}
    }

    h := &minHeap{}
    for _, x := range arr {
        if h.Len() < k {
            heap.Push(h, x)
        } else if x > (*h)[0] {
            heap.Pop(h)
            heap.Push(h, x)
        }
    }
    return []int(*h)
}

复杂度分析

  • 时间复杂度:$O(n\log(k+1))$。
  • 空间复杂度:额外空间 $O(k)$。

关键点总结

[!green]

维护大小至多为 k 的小顶堆,堆顶是当前最弱候选;新值大于堆顶时替换,从而始终保留最大的 k 次出现。

易错点总结

[!yellow]

  • 这里使用小顶堆,堆顶代表已保留候选中最小的一项。
  • 重复值按出现次数保留,不能用集合去重。
  • k=0 时不能访问堆顶;结果顺序不限,Go 直接返回堆数组即可。

相似题目

题目 难度 关联与区别
剑指 Offer 40. 最小的k个数 简单 都可维护容量为 k 的候选堆;该题选最小的 k 个数、使用大顶堆,本题选最大的 k 个数、使用小顶堆,重复值均按次数保留。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/820599718012
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!