1 题目
题目:前K大数(Top k Largest Numbers)
描述:在一个数组中找到前K大的数。
lintcode题号——544,难度——medium
样例1:
输入: [3, 10, 1000, -99, 4, 100] 并且 k = 3
输出: [1000, 100, 10]
样例2:
输入: [8, 7, 6, 5, 4, 3, 2, 1] 并且 k = 5
输出: [8, 7, 6, 5, 4]
2 解决方案
2.1 思路
该题可以用quick select的算法来将小于k的数都移到左边,再用quick sort快排进行排序。
也可以考虑使用优先序列来做,维护一个容量为k的优先序列,需要取前k个数,每次取完序列顶部再弹出,进行k次即可。
2.3 时间复杂度
使用quick select的方式,移动小于k的值到左边耗时O(n),对左边的k个数排序耗时O(k * log k),总时间复杂度为O(n + k * log k)。
使用优先序列的方式,将元素放入序列耗时O(log k),需要进行n次,将元素弹出序列耗时O(logk),需要进行k次,总时间复杂度O(n * log k) +O(k * log k) = O(n * log k);
在k趋近于n的时候,两种排序方式的耗时相近。
2.4 空间复杂度
使用quick select的方式,空间复杂度为O(1)。
使用优先序列的方式,需要一个容量为k的最小堆优先序列,所以空间复杂度为O(k)。
3 源码
细节:
- 使用优先序列的方式,建立一个最小堆优先序列,每次弹出堆顶的最小元素,保留k个较大的元素。
- 因为是最小堆优先序列,组装结果的时候需要按照弹出顺序的倒序来组装。
C++版本:
/**
* @param nums: an integer array
* @param k: An integer
* @return: the top k largest numbers in array
*/
vector<int> topk(vector<int> &nums, int k) {
// write your code here
vector<int> result;
if (nums.size() < k)
{
return result;
}
priority_queue<int, vector<int>, greater<int>> numQueue; // 最小堆优先序列
for (auto it : nums)
{
numQueue.push(it);
if (numQueue.size() > k) // 弹出多余的数
{
numQueue.pop();
}
}
for (int i = 0; i < k; i++)
{
result.insert(result.begin(), numQueue.top()); // 倒序组装结果
numQueue.pop();
}
return result;
}