题目:
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
分析:
利用堆排序,O(N logK),适合处理海量数据
(1) 遍历输入数组,将前k个数插入到推中;(利用multiset来做为堆的实现)
用前k个数字来建立大顶堆,而后拿后面的后面的n-k个元素依次与大顶堆中的最大值(即堆顶)元素比较,
(2) 继续从输入数组中读入元素做为待插入整数,并将它与堆中最大值比较:如果待插入的值比当前已有的最大值小,则用这个数替换当前已有的最大值;如果待插入的值比当前已有的最大值还大,则抛弃这个数,继续读下一个数。
如果小于该最大元素,则用该元素替换掉堆顶元素,并调整堆使其维持大顶堆的结构,如果大于该最大元素,则直接跳过,继续拿下一个数字与堆顶元素比较,等到所有的元素比较并操作完,这时数组中后面的元素都比该大顶堆中的数字要大,那么该大顶堆中的k个数字变为数组中最小的k个数字,且堆顶元素为这k个最小数组中最大的,因此它又是数组中第k小的数字。
这样动态维护堆中这k个数,以保证它只储存输入数组中的前k个最小的数,最后输出堆即可。
该算法建立大顶堆需要的时间为O(k),每次调整堆需要的时间为O(logk),而总共要调整n-k次,因此时间复杂度为
O(k+(n-k)logk),当k远远小于n时,时间复杂度可近似为O(nlogk)。
代码:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
vector<int> result;
int len = input.size();
if(input.empty() || k<=0 || len < k) return result;
multiset<int, greater<int> > leastNumbers; // 从大到小排序
multiset<int, greater<int> >::iterator iterGreater; // 定义迭代器
vector<int>::iterator iter = input.begin();
for(; iter != input.end(); ++iter)
{
// 将前k个数直接插入进multiset中,注意是小于K
if(leastNumbers.size() < k)
{
leastNumbers.insert(*iter);
}
else
{
// 因为设置的从大到小排序,故multiset中第一个位置的元素即为最大值
iterGreater = leastNumbers.begin();
// 如果input中当前元素比multiset中最大元素小,则替换;即保持multiset中这k个元素是最小的。
if(*iter < *(leastNumbers.begin()))
{
// 替换掉当前最大值
leastNumbers.erase(iterGreater);
leastNumbers.insert(*iter);
}
}
}
for(iterGreater = leastNumbers.begin();iterGreater!=leastNumbers.end();++iterGreater)
{
result.push_back(*iterGreater); // 将multiset中这k个元素输出
}
return result;
}