剑指Offer-时间效率面试题40:最小的k个数
Posted flix
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer-时间效率面试题40:最小的k个数相关的知识,希望对你有一定的参考价值。
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
思路1
可以先将这n个整数升序排序,然后输出前k个数字即可。下面的代码使用快速排序:
class Solution {
public:
void swap(vector<int>& v, int i, int j){
int t = v[i];
v[i] = v[j];
v[j] = t;
}
int partition(vector<int>& v, int left, int right){
int i = left;
int j = right;
int baseline = v[left];
while(i<j){
while(v[j]>=baseline && i<j) //先找合适的j
j--;
while(v[i]<=baseline && i<j) //再找合适的i
i++;
swap(v, i, j);
}
swap(v, left, i);
return i;
}
void quicksort(vector<int>& v, int left, int right){
if(left<right){
int idx = partition(v, left, right);
quicksort(v, left, idx-1);
quicksort(v, idx+1, right);
}
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> ans;
if(input.empty() || k<=0 || k>input.size())
return ans;
quicksort(input, 0, input.size()-1);
for(int i=0; i<k&&i<input.size(); i++)
ans.push_back(input[i]);
return ans;
}
};
由于使用快速排序,所以时间复杂度为O(nlogn).
思路2
第二种方法类似于面试题39:数组中出现次数超过一半的数字的解法,利用partition函数。首先将数组按照基准进行调整,将数组分为两部分,左边都小于基准,右边都大于基准,这样如果基准恰好是第k个数字(从第0个数字开始计数),那么前k个数字就是要求的解。如果基准的下标小于k(下标从0开始),那么对基准右边应用partition函数;如果基准的下标大于k,则对基准左边应用partition函数。重复这一过程,直至基准恰好是第k个数字。代码如下:
class Solution {
public:
void swap(vector<int>& v, int i, int j){
int t = v[i];
v[i] = v[j];
v[j] = t;
}
int partition(vector<int>& v, int left, int right){
int i = left;
int j = right;
int baseline = v[left];
while(i<j){
while(v[j]>=baseline && i<j)
j--;
while(v[i]<=baseline && i<j)
i++;
swap(v, i, j);
}
swap(v, left, i);
return i;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> ans;
if(input.empty() || k<=0 || k>input.size())
return ans;
int left = 0;
int right = input.size()-1;
int idx = partition(input, left, right);
while(idx!=k){
if(idx>k){
right = idx-1;
idx = partition(input, left, right);
}
else if(idx<k){
left = idx+1;
idx = partition(input, left, right);
}
}
for(int i=0; i<k; i++)
ans.push_back(input[i]);
return ans;
}
};
该算法的时间复杂度为O(n),该算法不能保证输出的k个最小数字是排序的。
思路3
上面的2中做法都会修改输入的数组。如果不允许修改输入的数组,我们可以使用如下思路:使用一个容量为k容器保存前k个数,遍历输入的数组,如果容器未满,则将下一个元素放进容器中;如果容器已满,则找到容器中最大的元素:如果容器最大的元素大于下一个元素,则删除容器中最大的元素并将下一个元素放进容器,否则保留容器中的数据不变。当容器已满时,我们需要进行3个操作:查找最大值、删除和插入。可以使用最大堆来实现这3个操作,在最大堆中,需要O(1)的时间来得到最大值,需要O(logk)的时间来进行插入和删除。如果可以使用stl的话,可以使用stl中的multiset作为容器,multiset基于红黑树实现,其查找、插入、删除均需要O(logk)的时间。下面使用multiset实现这一算法:
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> ans;
if(input.empty() || k<=0 || k>input.size())
return ans;
multiset<int, greater<int>> container;
for(int i=0; i<input.size(); i++){
if(container.size()<k){
container.insert(input[i]);
}
else{
multiset<int, greater<int>>::iterator iter = container.begin();
if(input[i]<*iter){
container.erase(iter);
container.insert(input[i]);
}
}
}
multiset<int, greater<int>>::iterator iter = container.begin();
while(iter!=container.end()){
ans.push_back(*iter);
iter++;
}
return ans;
}
};
该算法的时间复杂度为O(nlogk),为3个算法中时间复杂度最低的。该算法无需对数据进行移动,比较适合海量数据。
以上是关于剑指Offer-时间效率面试题40:最小的k个数的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 面试题 17.14. 最小K个数(堆排,快排)/剑指 Offer 10- I. 斐波那契数列 /470. 用 Rand7() 实现 Rand10()(拒绝采样,学!!!)
⭐算法入门⭐《堆》简单01 —— LeetCode 剑指 Offer 40. 最小的k个数
剑指 Offer 45. 把数组排成最小的数 剑指 Offer 61. 扑克牌中的顺子 剑指 Offer 40. 最小的k个数