【算法】 数据流中的第 K 大元素

45 篇文章 0 订阅
43 篇文章 0 订阅

难度:简单

题目:

设计一个找到数据流中第 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 <= 104
0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
最多调用 add 方法 104 次
题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素

解题思路

为了设计这样一个类,我们可以使用最小堆(优先队列)来实现。
最小堆是一种特殊的完全二叉树,其中每个父节点的值都小于或等于其子节点的值。
在本问题中,我们维护一个大小为k的最小堆,这样堆顶元素就是当前数据流中的第k大元素。
每次插入新元素时,若堆的大小未达到k,则直接将新元素添加到堆中;若堆的大小已经达到k,且新元素大于堆顶元素,则替换堆顶元素并重新调整堆。这样,堆顶元素始终是当前数据流中的第k大元素。

JavaScript实现:

class MinHeap {
    constructor() {
        this.heap = [];
    }

    insert(val) {
        this.heap.push(val);
        this.bubbleUp(this.heap.length - 1);
    }

    bubbleUp(index) {
        while (index > 0) {
            let parent = Math.floor((index - 1) / 2);
            if (this.heap[parent] <= this.heap[index]) break;
            [this.heap[parent], this.heap[index]] = [this.heap[index], this.heap[parent]];
            index = parent;
        }
    }

    extractMin() {
        const min = this.heap[0];
        this.heap[0] = this.heap.pop();
        this.bubbleDown(0);
        return min;
    }

    bubbleDown(index) {
        while (true) {
            let leftChild = 2 * index + 1;
            let rightChild = 2 * index + 2;
            let smallest = index;

            if (leftChild < this.heap.length && this.heap[leftChild] < this.heap[smallest]) {
                smallest = leftChild;
            }
            if (rightChild < this.heap.length && this.heap[rightChild] < this.heap[smallest]) {
                smallest = rightChild;
            }
            if (smallest === index) break;

            [this.heap[smallest], this.heap[index]] = [this.heap[index], this.heap[smallest]];
            index = smallest;
        }
    }
}

class KthLargest {
    constructor(k, nums) {
        this.k = k;
        this.minHeap = new MinHeap();
        nums.forEach(num => this.minHeap.insert(num));
        while (this.minHeap.heap.length > k) {
            this.minHeap.extractMin();
        }
    }

    add(val) {
        if (this.minHeap.heap.length < this.k) {
            this.minHeap.insert(val);
        } else if (val > this.minHeap.heap[0]) {
            this.minHeap.extractMin();
            this.minHeap.insert(val);
        }
        return this.minHeap.heap[0];
    }
}

这里首先定义了一个MinHeap类,用于实现最小堆的功能,包括插入元素、提取最小元素以及调整堆。然后在KthLargest类中,构造函数接受一个整数k和一个初始的整数数组nums,初始化最小堆并填充nums中的元素,保证堆的大小不超过k。add方法用于向数据流中添加新元素,并返回当前第k大的元素。如果堆未满,则直接插入;如果堆已满且新元素更大,则替换堆顶元素并重新调整堆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值