原文链接: https://leetcode-cn.com/problems/kth-largest-element-in-a-stream
英文原文
Design a class to find the kth
largest element in a stream. Note that it is the kth
largest element in the sorted order, not the kth
distinct element.
Implement KthLargest
class:
KthLargest(int k, int[] nums)
Initializes the object with the integerk
and the stream of integersnums
.int add(int val)
Appends the integerval
to the stream and returns the element representing thekth
largest element in the stream.
Example 1:
Input ["KthLargest", "add", "add", "add", "add", "add"] [[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]] Output [null, 4, 5, 5, 8, 8] Explanation 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
Constraints:
1 <= k <= 104
0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
- At most
104
calls will be made toadd
. - It is guaranteed that there will be at least
k
elements in the array when you search for thekth
element.
中文题目
设计一个找到数据流中第 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
个元素
通过代码
高赞题解
各位题友大家除夕好! 今天是 @负雪明烛 坚持日更的第 18 天。今天力扣上的每日一题是「703. 数据流中的第 K 大元素」。
解题思路
首先,面试题警告:
- 本题「数据流中的 TopK」是我参加亚马逊面试遇到过的真题。
- 另外,实现堆算法是我参加微软面试遇到的真题。
- 多说一句,TopK 算法在面试中常问,推荐阅读「拜托,面试别再问我TopK了!!!」
本题是我们求在一个数据流中的第 $K$ 大元素。所谓数据流,即是说我们写的算法需要支持 add()
函数;在力扣后台评测程序中会多次调用add()
函数,每次调用都会向我们写的算法中添加一个元素。而题目要求的就是在每次 add()
之后,整个数据流(包括初始化的元素和所有 add 进来的元素)中的第 $K$ 大元素。
先说一个最暴力的解法:我们底层数据结构使用数组实现,当每次调用 add()
函数时,向数组中添加一个元素,然后调用 sort()
函数进行排序,返回排序后数组的第 $K$ 个数字。该做法在每次调用 add()
函数时的时间复杂度为 $O(K*log(K))$ ,该时间复杂度太高,当 $K$ 很大 / add()
调用次数太多的时候,一定会超时。
从上面的分析中,我们已经看出来了,使用数组的核心问题是:数组自身不带排序功能,只能用 sort()
函数,导致时间复杂度过高。
因此我们考虑使用自带排序功能的数据结构——堆。
在大根堆(图一)中,父节点的值比每一个子节点的值都要大。在小根堆(图二)中,父节点的值比每一个子节点的值都要小。
本题的操作步骤如下:
- 使用大小为 $K$ 的小根堆,在初始化的时候,保证堆中的元素个数不超过 $K$ 。
- 在每次
add()
的时候,将新元素push()
到堆中,如果此时堆中的元素超过了 $K$,那么需要把堆中的最小元素(堆顶)pop()
出来。 - 此时堆中的最小元素(堆顶)就是整个数据流中的第 $K$ 大元素。
问答:
- 为什么使用小根堆?
- 因为我们需要在堆中保留数据流中的前 $K$ 大元素,使用小根堆能保证每次调用堆的
pop()
函数时,从堆中删除的是堆中的最小的元素(堆顶)。
- 为什么能保证堆顶元素是第 $K$ 大元素?
- 因为小根堆中保留的一直是堆中的前 $K$ 大的元素,堆的大小是 $K$,所以堆顶元素是第 $K$ 大元素。
- 每次
add()
的时间复杂度是多少?
- 每次
add()
时,调用了堆的push()
和pop()
方法,两个操作的时间复杂度都是 $log(K)$.
代码
使用 Python2 写的代码如下。
class KthLargest(object):
def __init__(self, k, nums):
"""
:type k: int
:type nums: List[int]
"""
self.k = k
self.que = nums
heapq.heapify(self.que)
def add(self, val):
"""
:type val: int
:rtype: int
"""
heapq.heappush(self.que, val)
while len(self.que) > self.k:
heapq.heappop(self.que)
return self.que[0]
# Your KthLargest object will be instantiated and called as such:
# obj = KthLargest(k, nums)
# param_1 = obj.add(val)
刷题心得
- 本题是堆的经典运用,在面试中可能会遇到,请认真对待本题,包括「TopK问题」;
- 数据流的题目还是很有意思的,力扣上有其他数据流题目,建议也做一下。
参考资料:
OK,以上就是 @负雪明烛 写的今天题解的全部内容了,如果你觉得有帮助的话,求赞、求关注、求收藏。如果有疑问的话,请在下面评论,我会及时解答。
关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。
祝大家过年好!我们明天再见!
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
62989 | 122688 | 51.3% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|
相似题目
题目 | 难度 |
---|---|
数组中的第K个最大元素 | 中等 |