版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/82631155
面试题40:最小的k个数
输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
解法1
还是用Partition函数,存在这样的划分,正好取到第k小的数字为基准数,能够使比其小的数字都在其左边,就找到了最小的k个数。
#include<bits/stdc++.h>
#include "../Utilities/Array.h"
using namespace std;
//输入长为n的input数组,将最小的k个数字写入长为k的output数组
void GetLeastNumbers_Solution1(int* input, int n, int* output, int k) {
//非空校验,注意k是不能大于n的
if(input == nullptr || output == nullptr || k > n || n <= 0 || k <= 0)
return;
//先从头到尾随机划分一下
int start = 0;
int end = n - 1;
int index = Partition(input, n, start, end);
//当划分到的位置不是第k个位置(从0开始下标k-1)
while(index != k - 1) {
if(index > k - 1) {//如果大了
//就在左边继续划分着找
end = index - 1;
index = Partition(input, n, start, end);
} else {//如果小了
//就在右边继续划分着找
start = index + 1;
index = Partition(input, n, start, end);
}
}
//找完以后,最左边的k个数字就是最小的k个数字
for(int i = 0; i < k; ++i)
output[i] = input[i];
}
int main() {
int data[] = {4, 5, 1, 6, 2, 7, 3, 8};
int out[4];
GetLeastNumbers_Solution1(data,8,out,4);
for(int i=0;i<4;i++)
cout<<out[i]<<"\t";
return 0;
}
解法2
解法1需要整个数组都读出来,解法2可以边读边去检查处理。适合处理海量数据,因为海量数据没法一次读到内存里。
因为只要有能保存k个小数字的容器把它保存下来就可以了,可以用能容纳k个数字的最大堆,如果在堆满了以后读到比堆顶更小的,就把它替换下来,这样最终堆里的k个数字就是最小的k个数。
书上指出堆不容易实现,可以用实现了红黑树的容器。红黑树的插入、删除、查找都是O(logk)的时间,使用STL中的multiset
,set
也实现了RBT,但是它是不允许元素重复的,而multiset
允许。
#include<bits/stdc++.h>
#include "../Utilities/Array.h"
using namespace std;
//typedef用来声明别名:"typedef 复杂的东西 别名;"
//greater表示内置类型从大到小排序,即第一个元素一定是最大的
//set和multiset会根据特定的排序原则将元素排序,这里即模拟了最大堆
typedef multiset<int, greater<int> > intSet;
typedef multiset<int, greater<int> >::iterator setIterator;
//传入数组,取最小的k个元素的multiset容器,k的值
//找到数组中最小的k个元素,放入容器中供调用者使用
void GetLeastNumbers_Solution2(const vector<int>& data, intSet& leastNumbers, int k) {
leastNumbers.clear();//清空容器
//检查输入合法性
if(k < 1 || data.size() < k)
return;
//从数组前面开始
vector<int>::const_iterator iter = data.begin();
for(; iter != data.end(); ++ iter) {//向后遍历整个数组
if((leastNumbers.size()) < k)//如果容器还没满
leastNumbers.insert(*iter);//直接插入容器中
else {//如果容器已经满了
//取排序multiset的第一个元素,即逻辑最大堆的堆顶
setIterator iterGreatest = leastNumbers.begin();
//如果当前这个数比最大堆堆顶还小
if(*iter < *(leastNumbers.begin())) {
leastNumbers.erase(iterGreatest);//把堆顶移出去
leastNumbers.insert(*iter);//把它放进来
}
}
}
}
int main() {
int data[] = {4, 5, 1, 6, 2, 7, 3, 8};
//写入vector数组
vector<int> vectorData;
for(int i = 0; i < 8; ++ i)
vectorData.push_back(data[i]);
//multiset容器
intSet leastNumbers;
GetLeastNumbers_Solution2(vectorData,leastNumbers,4);
//在这个容器里是从大到小有序的,使用真的最大堆时不能保证有序
for(setIterator iter = leastNumbers.begin(); iter != leastNumbers.end(); ++iter)
cout<<*iter<<"\t";
return 0;
}