每日一题:703. 数据流中的第 K 大元素

73 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第29天,点击查看活动详情

一、题目描述:

703. 数据流中的第 K 大元素 - 力扣(LeetCode)

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

请实现 KthLargest 类:

  • KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
  • int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。  

示例:

输入:
["KthLargest", "add", "add", "add", "add", "add"]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]

解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3);   // return 4
kthLargest.add(5);   // return 5
kthLargest.add(10);  // return 5
kthLargest.add(9);   // return 8
kthLargest.add(4);   // return 8

提示:

  • 1 <= k <= 10^4
  • 0 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • -10^4 <= val <= 10^4
  • 最多调用 add 方法 10^4 次
  • 题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素

二、思路分析:

简单分析下为什么用最小堆,能得到数据流中第k大元素:

假设某个时刻堆中有k个元素,为前k大的元素,此时新来一个元素val

  • 如果val比堆顶元素小,说明val一定不是前k大的元素,且val永远不可能成为前k大元素,可以直接丢弃
  • 如果val比堆顶元素大,说明val是前k大的元素中的一个,而堆顶元素不会再是前k大的元素了,将堆顶元素丢弃。此时从取出堆顶元素,就是滴k大的元素

也可以从反面思考:假设数据流总共有n个元素,遍历到第k+1个元素和往后时,每次一定会丢弃一个不在前k大的元素,那么剩下的元素一定就是前k大的

三、AC 代码:


class KthLargest {

    private PriorityQueue<Integer> pq = new PriorityQueue<>();
    private int k;

    public KthLargest(int k, int[] nums) {
        this.k = k;
        for (int num : nums){
            if (pq.size() < k) {
                pq.offer(num);
                continue;
            }

            if (num > pq.peek()){
                pq.poll();
                pq.offer(num);
            }
        }
    }

    public int add(int val) {
        if (pq.size() < this.k) {
            pq.offer(val);
            return pq.peek();
        } 

        if (val > pq.peek()){
            pq.poll();
            pq.offer(val);
        }

        return pq.peek();
    }
}

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest obj = new KthLargest(k, nums);
 * int param_1 = obj.add(val);
 */