剑指offer:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路一:用一个multiset存储k个数,后面得到一个数,如果得到的数字比leastNumbers中的最大值大,则舍去,否则删掉最大值,插入得到的数到multiset中。直到所有的n-k数走一遍。则multiset中的k个数便是n个数中最小的k个数。
由于O(1)时间得到最大值,插入和删除的时间复杂度都是O(logK)时间。而需要n次操作,所以总的时间复杂度是O(nlogK)
typedef multiset<int,greater<int>> intSet;//greater<int>表示第一个元素(根)是最大的
typedef multiset<int,greater<int>>::iterator setIterator;
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
intSet leastNumbers;//leastNumbers用于存储k个元素
vector<int> result;
if(k < 1 || (input.size()) < (unsigned long)k)//判断输入是否有意义
return result;
vector<int>::const_iterator iter = input.begin();
for(; iter != input.end(); iter++){
if((leastNumbers.size()) < (unsigned long)k)//将前k个数插入leastNumbers中
leastNumbers.insert(*iter);
else{//将后面的进行比较,如果得到的数字比leastNumbers中的最大值大,则舍去,否则删掉最大值,插入得到的数字
setIterator iterGreatest = leastNumbers.begin();
if(*iter < *(leastNumbers.begin())){
leastNumbers.erase(iterGreatest);
leastNumbers.insert(*iter);
}
}
}
while(!leastNumbers.empty()){//将multiset中的数转存到vector中,时从大到小的排列
setIterator iterGreatest = leastNumbers.begin();
result.push_back(*iterGreatest);
leastNumbers.erase(iterGreatest);
}
return result;
}
};
思路二:利用快速排序算法给我们的启发,如果基于数组的第K个数字来调整,使得比K个数字小的所有数字都位于数组的左边,比第K个数字大的所有数字都位于数组的右边。
怎么实现呢?首先选择一个基准,实现基准左边的都小于等于基准,右边的都大于等于基准。再看基准是否在第k-1个位置,如果在k-1左边,则继续右边的操作,否则继续左边的操作。
由于次需要n次比较,但是只需要几次就可以了。时间复杂度是O(n)
但是不适合大规模的数字,因为内存不够。而且会改动输入数组。
这个代码就不写了。
两种思路的比较:
基于快速排序 基于multiset
时间复杂度 O(n) O(nlogK)
是否需要修改输入数组 是 否
是否适用于海量数据 否 是