快速选择算法(求解第k大)
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速选择算法(求解第k大)相关的知识,希望对你有一定的参考价值。
快速选择算法(求解第k大)
快速选择算法基于快排。
复习一波快排:
void quick_sort(int l,int r){
if(l<r){
int i=l,j=r,x=a[l];//x是基数.
while(i<j){
while(i<j&&a[j]>x) j--; //从右往左找到第一个小于等于x的
if(i<j) a[i++]=a[j];//填到左边i
while(i<j&&a[i]<x) i++;//从左往右找到第一个大于等于x的
if(i<j) a[j--]=a[i];//填到右边j
}a[i]=x;
quick_sort(l,i-1);//递归
quick_sort(i+1,r);
}
}
- 选择一个基数
- 把比该基数小的放到左边,大的放到右边。
- 递归左、右部分。
因此我们只需要知道第 k k k大是多少,我们并不关心它的左、右部分是否有序。
所以基于快排,我们可以对时间复杂度进行优化。
- 每次随机选择一个基数,这里用随机的原因,如果每次选最大或者最小的话,时间复杂度最坏会达到 O ( n 2 ) O(n^2) O(n2)。
- 然后把该基数交换到最右部分。
- 遍历
[
l
,
r
)
[l,r)
[l,r),用两个指针
i
,
j
i,j
i,j,开始都指向
l
l
l。
- 若当前 a [ j ] ≤ x a[j]\\le x a[j]≤x,就交换 ( a [ i ] , a [ j ] ) (a[i],a[j]) (a[i],a[j]),然后两个指针都向右移动一位。
- 否则, j j j指针向右移动一位。
- 当遍历完之后,交换 a [ i ] , a [ r ] a[i],a[r] a[i],a[r],此时 i i i左边的元素都小于等于它,右边的都大于它。
- 然后返回位置
i
i
i,即该元素是第
i
i
i小的。
- 若该位置 i i i是我们要找到第 i i i小,则直接返回。
- 否则若 i < p o s i<pos i<pos,则往右递归,否则往左递归。
- 这样每次只用递归一个部分,不用像快排那样递归两个部分。
时间复杂度: O ( n ) O(n) O(n)
事实上 S T L STL STL已经帮我们实现了 ( v o i d ) n t h _ e l e m e n t ( s t a r t , p o s , e n d ) (void)\\ nth\\_element(start,pos,end) (void) nth_element(start,pos,end)
nth_element(a.begin(),a.begin()+k-1,a.end())
return a[k-1]; //第k小
nth_element(a.begin(),a.begin()+k-1,a.end(),greater<int>())
return a[k-1]; //第k大
自己实现第 k k k大。
int partition(vector<int>&a,int l,int r){
int i = rand()%(r-l+1)+l;
swap(a[i],a[r]);
int x=a[r];i=l;
for(int j=l;j<r;j++){
if(a[j]<=x){
swap(a[i++],a[j]);
}
}
swap(a[i],a[r]);
return i;
}
int nth(vector<int>&a,int l,int r,int p){
int q=partition(a,l,r);
if(q==p) return a[q];
else return q<p?nth(a,q+1,r,p):nth(a,l,q-1,p);
}
int solve(vector<int> &a,int k){
srand(time(0));
return nth(a, 0, a.size() - 1, a.size() - k);
}
以上是关于快速选择算法(求解第k大)的主要内容,如果未能解决你的问题,请参考以下文章