常见的Top k问题,下面以多种解法来解决这个问题
下面拿一道Leetcode上的题目来练练手。
面试题40. 最小的k个数
题意:返回最小的k个数
快速排序
其实最简单的思路是排一下序,然后把前k个返回
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
sort(arr.begin(),arr.end());
vector<int> res;
for(int i=0;i<k;i++) res.push_back(arr[i]);
return res;
}
};
平均时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
空间复杂度 O(k)
大根堆
维护一个大小为k的大根堆,根的首部元素是最大的,先把前k个插入,然后将剩下的元素逐个与堆的顶部元素比较,如果小于的话,就弹出顶部元素,插入该元素,最后将堆的全部元素弹出到数组返回。
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> res;
if(k==0) return res;
priority_queue<int,vector<int>,less<int>> heap;
for(int i=0;i<k;i++){
heap.push(arr[i]);
}
for(int i=k;i<arr.size();i++){
if(arr[i]<heap.top()){
heap.pop();
heap.push(arr[i]);
}
}
while(heap.size()){
res.push_back(heap.top());
heap.pop();
}
return res;
}
};
其实一开始我用mutiset实现了一个大根堆,效果是一样的。
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
multiset<int> s;
vector<int> res;
if(k==0) return res;
int i;
for(i=0;i<k;i++){
s.insert(arr[i]);
}
for(;i<arr.size();i++){
set<int>::iterator it = s.end();
it--;
if(arr[i]<*it){
s.erase(it);
s.insert(arr[i]);
}
}
set<int>::iterator it = s.begin();
for(;it!=s.end();it++){
res.push_back(*it);
}
return res;
}
};
堆的插入和删除都是 O ( l o g n ) O(logn) O(logn),所以最坏时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度是 O ( k ) O(k) O(k)
nth_element
nth_element(arr.begin(),arr.begin()+k,arr.end())
用来求第k+1小的元素,也就是arr[k]归位,比arr[k]小的都在其左边,比它大的都在它右边,但不保证有序,平均时间复杂度
O
(
n
)
O(n)
O(n)
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
nth_element(arr.begin(),arr.begin()+k,arr.end());
arr.resize(k);
return arr;
}
};
partition
采用快排一次划分的思想,并加入随机化,平均复杂度为 O ( n ) O(n) O(n)
参考:最小的k个数
typedef unsigned long long ull;
class Solution {
public:
int partition(vector<int>& arr, int low, int high){
int pivot = arr[low];
while(low<high){
while(low<high&&arr[high]>=pivot) --high;
arr[low] = arr[high];
while(low<high&&arr[low]<pivot) ++low;
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
int randomized_partition(vector<int>& arr,int low,int high){
int index = rand()%(high-low+1)+low;
swap(arr[index],arr[low]);
return partition(arr,low,high);
}
void randomized_select(vector<int>& arr,int low,int high,int k){
if(low>high) return ;
int pos = randomized_partition(arr,low,high);
if(pos-low+1==k) return ;
else if(pos-low+1>k) randomized_select(arr,low,pos-1,k);
else randomized_select(arr,pos+1,high,k-(pos-low+1));
}
vector<int> getLeastNumbers(vector<int>& arr, int k) {
srand(unsigned(time(NULL)));
randomized_select(arr,0,arr.size()-1,k);
arr.resize(k);
return arr;
}
};
BFPRT
O(n)的算法求topk,待补。