题目:
输入n个整数,找出其中最小的K个数。
例子
例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
链接:
剑指Offer(第2版):P209
思路标签:
- 算法:快排分区、堆排序
- 数据结构:红黑树
解答:
1. 基于快排Partition函数的算法(时间复杂度:O(n))
- 基于快排算法中的Partition函数,使得比选中的数字小的数字都在它左边,比选中的数字大的数字都在它的右边。
- 如果选中的数字的下标刚好是k-1,那么这个数字就是数组中的中位数;
- 如果它的下标大于k-1,那么中位数应该位于它的左边,继续在左边寻找;
- 如果它的下标小于k-1,那么中位数应该位于它的右边,继续在右边寻找。
- 利用递归实现,但是会修改输入数组的顺序,这里需要问清面试官。
class Solution {
public:
void swap(int &i, int &j)
{
if (i == j)
return;
int temp = i;
i = j;
j = temp;
}
int Partition(vector<int> &input, int start, int end)
{
if (input.empty() || start>end) return -1;
int index = rand() % (end - start + 1) + start;
swap(input[index], input[end]);
int small = start - 1;
for (index = start; index<end; ++index)
{
if (input[index] <= input[end])
{
++small;
if (index != small)
swap(input[index], input[small]);
}
}
small++;
swap(input[small], input[end]);
return small;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
vector<int> result;
if (input.empty() || k>input.size() || k <= 0) return result;
int start = 0;
int end = input.size() - 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);
}
}
for (int i = 0; i<k; ++i)
{
result.push_back(input[i]);
}
return result;
}
};
2. 堆排序,适合处理海量数据(时间复杂度O(nlgk))
- 利用一个大小为k的数据容器来存储最小的k个数字,如果容器中的数字小于k个,那么直接将数据放入;
- 如果容器中已经存在k个数字,则用待插入的整数和容器中的最大值进行比较,小于最大值则替换,否则输入下一个数字。
- 很明显利用最大堆的原理来进行求解。
- 可以直接利用STL中的set和multiset,二者是利用红黑树实现的。
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k)
{
vector<int> result;
int length = input.size();
if (input.empty() || k < 1 || length < k)
return result;
multiset<int, greater<int>> leastNumbers; //从大到小排序的set
multiset<int, greater<int>>::iterator iterGreater; //定义迭代器
vector<int>::const_iterator iter = input.begin();
for (; iter != input.end(); ++iter) {
if (leastNumbers.size() < k)
leastNumbers.insert(*iter);
else {
iterGreater = leastNumbers.begin();
if (*iter < *(iterGreater)) {
leastNumbers.erase(iterGreater);
leastNumbers.insert(*iter);
}
}
}
for (iterGreater = leastNumbers.begin(); iterGreater != leastNumbers.end(); ++iterGreater)
result.push_back(*iterGreater);
return result;
}
};
C++相关
C++ STL set容器常用用法
【C++ STL】Set和Multiset