题目描述:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
这是一个经典的topK问题。
根据一般的想法,对数组进行排序,再去前K个数作为结果。排序算法若采用快排,平均时间复杂度为O(n*logn)。
因此常用解法为构建最大(小)堆的策略来解topK问题,这种解法的平均时间复杂度为O(n*logk),所以很适合于海量数据中的topK的问题。思路如下:
1、建立容量为k的最大堆,最大堆的定义为树的根节点的值为最大值。
2、遍历输入数组,若最大堆的容量未满,则继续将数组中的元素放入最大堆中。
3、若最大堆已满,则根据数组中下一个元素与堆中最大元素比较,若大于最大元素则丢弃;若小于最大元素,则将堆中的最大元素删掉erase,并将数组的该元素放入堆中insert,得到重新排序后的最大堆。
考虑到面试过程中时间限制,无法重现最大堆的算法,因此常用STL中的multiset容器来实现最大堆,保证能快速实现查询、删除、插入的操作。multiset底层是采用红黑树来实现的,可以通过O(logk)的时间实现上述三步,对于数组中的n个数则时间复杂度为O(n*logk)。
在处理海量数据(上千万or亿条数据)时,相比于普通的排序后取数,时间复杂度为O(n*logn)的方法,最大堆算法的时间复杂度O(n*logk)将减少几十个数量级。
代码如下:
class Solution {
public:
typedef multiset<int, greater<int>> intset;
typedef multiset<int, greater<int>>::iterator setiterator;
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
intset leastnum;
return GetLeastNumbers_Solution(input,k,leastnum);
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k, intset& leastNumbers) {
vector<int> rs;
leastNumbers.clear();
int size = input.size();
if (k < 0 || size < k)
return rs;
vector<int>::iterator iter = input.begin();
for (; iter < input.end(); ++iter)
{
if (int(leastNumbers.size()) < k)
{
leastNumbers.insert(*iter);
}
else
{
if (*iter < *leastNumbers.begin())
{
leastNumbers.erase(leastNumbers.begin());
leastNumbers.insert(*iter);
}
}
}
while (!leastNumbers.empty())
{
rs.push_back(*leastNumbers.begin());
leastNumbers.erase(leastNumbers.begin());
}
rs.reserve(rs.size());
return rs;
}
};