题目
输入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个数字不一定是排序的)。
class Solution {
public:
//快速排序的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;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int n = input.size();
if (0 == n || k >n || k<=0) return vector<int>();
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);
}
}
vector<int> res(input.begin(), input.begin() + k);
return res;
}
};
解法二
(时间复杂度为O(nlogk)):不必一次载入所有的数据到内存,适合从海量的数据中找出最小的k个数
思想:用multiset保存最小的k个数字。每次读入一个数,如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中;如果容器中已有k个数字,则比较读入的数字和容器中最大的数字,如果前者大,则在容器中用前者替换后者;如果后者大,则前者不可能是最下的k个数之一
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);
}
}
}
}