题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
题解
- 利用最大堆(时间复杂度nlogk)来解决,维护一个包含4个元素的最大堆,首先取待处理数组的前k个元素构成最大堆,然后遍历数组的剩余元素,若当前元素比最大堆的堆顶(最大的元素)小,则将当前元素与最大堆的堆顶互换,再重新生成新的最大堆。遍历完所有元素后所得的最大堆即为数组中最小的k个数。
代码1:不用algorithm中已有的堆处理算法
#include<vector>
using namespace std;
void mySwap(int *pa, int *pb)//交换两个数
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
void downToMaxHeap(vector<int> &arr, int bgn, int end){//自顶向下移动构成局部最大堆,bgn表示当前父节点的位置
int child;//子节点在数组中的索引
int parent = bgn;//父节点在数组中的索引
while((child = parent * 2 + 1) < end){//保证父节点的值大于子节点
if((child < end - 1) && (arr[child] < arr[child+1]))
++child;//转移至右子节点
if(arr[child] > arr[parent])
mySwap(&arr[child], &arr[parent]);//若子节点的最大值大于父节点的值,将该子节点与父节点交换
else
break;
parent = child;//父节点转移至与其交换的子节点位置
}
}
void buildMaxHeap(vector<int> &arr, int bgn, int end){//将数组构造为最大堆
if(bgn >= end - 1)
return;
int parent = end / 2 - 1;//父节点的最大索引 (right-1)/2,right是数组最后一个元素(最深层最右边的叶子节点)的索引
while(parent >= 0){//父节点向前移动直至根节点
downToMaxHeap(arr, parent, end);//对每个父节点构造局部最大堆
--parent;
}
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
int length = input.size();
vector<int> result;
if(length <=0 || k <= 0 || length < k)
return result;
result.assign(input.begin(), input.begin()+k);//取输入数组的前k个元素
buildMaxHeap(result, 0, result.size());//构造最大堆
for(int i = k; i < length; ++i){
if(input[i] < result[0]){//若该元素小于堆顶,则与堆顶交换
mySwap(&input[i], &result[0]);
downToMaxHeap(result, 0, result.size());//从堆顶自上而下构造新的最大堆
}
}
return result;
}
代码2:利用algorithm中已有的堆排序算法
#include<vector>
#include<algorithm>
using namespace std;
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int length = input.size();
vector<int> result;
if(length <=0 || k <= 0 || length < k)
return result;
result.assign(input.begin(), input.begin()+k);
//for(int i = 0; i < k; ++i)
// result.push_back(input[i]);
make_heap(result.begin(), result.end());
for(int i = k; i < length; ++i){
if(input[i] < result[0]){
pop_heap(result.begin(), result.end());
result.pop_back();
result.push_back(input[i]);
push_heap(result.begin(), result.end());
}
}
sort_heap(result.begin(), result.end());
return result;
}
- 利用快速排序的思想,使得比第k个元素小的元素都位于其左边,比第k个元素大的元素都位于其右边,则数组的前k个数即为最小的k个数
代码:
#include<vector>
using namespace std;
int Partition(vector<int> &input, int begin, int end){
int low = begin;
int high = end;
int pivot = input[low];//选第一个元素为基准
while(low < high){
while(low < high && pivot <= input[high])
--high;
input[low] = input[high];
while(low < high && pivot >= input[low])
++low;
input[high] = input[low];
}
input[low] = pivot;
return low;//返回移动后基准的下标
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k){
int length = input.size();
//vector<int> result;
if(length <= 0 || k <= 0 || length < k)
return vector<int>();
int start = 0;
int end = length - 1;
int index = Partition(input, start, end);
while(index != (k-1)){
if(index > k - 1){
end = index - 1;
index = Partition(input, start, end);
}
else{
start = index + 1;
index = Partition(input, start, end);
}
}
vector<int> result(input.begin(), input.begin()+k);
return result;
}