输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。
解法一(平均时间复杂度为O(n)):
思想:基于partion函数来解决这个问题。如果基于数组的第k个数字来调整,使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的所有数字都位于数组的右边。这样调整之后,位于数组中左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)。
C++代码:
#include <iostream>
#include <vector>
using namespace std;
//快速排序的partition函数
int partition(vector<int> &input, int low, int high)
{
int pivotkey = input[low];
while (low <high)
{
while (low < high && pivotkey <= input[high]) --high;
input[low] = input[high];
while (low < high && pivotkey >= input[low]) ++low;
input[high] = input[low];
}
input[low] = pivotkey;
return low;
}
//在数组input中找到最小的k个数,结果保存在output中
void getLeastNumber(vector<int> &input, vector<int> &output, int k)
{
int n = input.size();
if (0 == n || k >n || k<=0) return;
int low = 0;
int high = n-1;
int index = partition(input, low, high);
//下标为index的不是第k小的数
while (index +1 != k)
{
//第k个数在index的右边
if (index+1 < k)
{
low = index+1;
index = partition(input, low, high);
}
else
{
high = index - 1;
index = partition(input, low, high);
}
}
for (int i=0;i<k;++i)
{
output.push_back(input[i]);
}
}
int main()
{
vector<int> input;
input.push_back(4);
input.push_back(5);
input.push_back(1);
input.push_back(6);
input.push_back(2);
input.push_back(7);
input.push_back(3);
input.push_back(8);
vector<int> output;
getLeastNumber(input, output, 4);
for (int i=0;i<output.size();++i)
{
cout<<output[i]<<' ';
}
cout<<endl;
return 0;
}
运行结果:3、2、1、4.
注:partition函数会调整数组中数字的顺序。
解法二(时间复杂度为O(nlogk)):
思想:用multiset保存最小的k个数字。每次读入一个数,如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中;如果容器中已有k个数字,则比较读入的数字和容器中最大的数字,如果前者大,则在容器中用前者替换后者;如果后者大,则前者不可能是最下的k个数之一。
C++ 代码:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
typedef multiset<int, greater<int>> intSet; //这里我们采用递减排序。默认的是less<key>,默认采用的是递增排序
typedef multiset<int, greater<int>>::iterator setIterator;
//在data中寻找最小的k个数保存到leastNumbers中
void getLeastNumbers(const vector<int> &data, intSet &leastNumbers, int k)
{
leastNumbers.clear();
if (k<1 || data.size() < k)
return;
vector<int>::const_iterator it = data.begin();
for (;it!=data.end();++it)
{
//如果leastNumbers中少于k个数
if (leastNumbers.size() < k)
{
leastNumbers.insert(*it);
}
else
{
//如果该数比leastNumbers中最大的数小,则替换
if (*it < *(leastNumbers.begin()))
{
leastNumbers.erase(leastNumbers.begin());
leastNumbers.insert(*it);
}
}
}
}
int main()
{
vector<int> input;
input.push_back(4);
input.push_back(5);
input.push_back(1);
input.push_back(6);
input.push_back(2);
input.push_back(7);
input.push_back(3);
input.push_back(8);
intSet output;
getLeastNumbers(input, output, 4);
for (setIterator it=output.begin();it!=output.end();++it)
{
cout<<*it<<' ';
}
cout<<endl;
return 0;
}
运行结果:4、3、2、1
两种算法的比较:
(1)第一中算法平均时间复杂度为O(n),比第二种思路快,但它会修改输入的数组。
(2)第二种算法不必一次载入所有的数据到内存,适合从海量的数据中找出最小的k个数。