在问 · 算法题解

703. 数据流中的第 K 大元素

题目描述

设计类 KthLargest,它维护一个不断增长的整数集合,并能随时给出其中第 k 大的数。这里的第 k 大按把所有数从大到小排好后的第 k 个位置来算,相同的值各占一个位置。

KthLargest(k, nums):用整数 k 和初始数组 nums 创建对象。add(val):把 val 加入集合,然后返回当前集合中第 k 大的数。

测试数据以两个数组给出:方法名序列和对应参数;输出数组是每次调用的返回值,构造函数对应 null。

示例 1
输入:操作 = ["KthLargest","add","add","add","add","add"]
参数 = [[3,[4,5,8,2]],[3],[5],[10],[9],[4]]
输出:[null,4,5,5,8,8]

解释:初始为 [4,5,8,2],k = 3;依次加入 3、5、10、9、4 后,第 3 大的数分别是 4、5、5、8、8。

示例 2
输入:操作 = ["KthLargest","add","add","add","add"], 参数 = [[4,[7,7,7,7,8,3]],[2],[10],[9],[9]]
输出:[null,7,7,7,8]

解释:k = 4,加入 2、10、9 后第 4 大仍为 7,再加入一个 9 后变为 8。

数据范围

  • 0 <= nums.length <= 10^4
  • 1 <= k <= nums.length + 1
  • -10^4 <= nums[i] <= 10^4
  • -10^4 <= val <= 10^4
  • add 最多调用 10^4 次。

解题思路

只保留最大的 k 个数,放在一个大小为 k 的最小堆里。堆顶是这 k 个数里最小的那个,正好就是全体的第 k 大。新数到来先压入堆,若堆超过 k 个,就弹出堆顶:被弹出的数至少比 k 个数小,以后再也不可能成为第 k 大。

为什么正确

每次 add 返回前,堆里恰好是目前最大的 k 个数(数量不足 k 时是全部),堆顶就是第 k 大。

复杂度分析

每次 add 时间 O(log k),构造 O(n log k);空间 O(k)。

易错点

要用最小堆而不是最大堆:我们关心的是“前 k 大里最小的那个”。另外初始 nums 可能少于 k 个,不能在构造时假设堆已满。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class KthLargest:
    def __init__(self, k: int, nums: List[int]):
        self.k = k
        self.heap = []
        for x in nums:
            self.add(x)

    def add(self, val: int) -> int:
        heapq.heappush(self.heap, val)
        if len(self.heap) > self.k:
            heapq.heappop(self.heap)
        return self.heap[0]

载入编辑器,自己跑一遍 →

设计 专题的其他题目

查看 设计 全部题目 →