快速选择算法

先上传送门

其实这题大可不必用快读O(nlogn)的算法,因为夹杂了太多的重复计算

我们只需要将小于该元素的放在左边,大于他的放在右边即可

因此引入快速选择算法,该算法可求第k大(小)元素,这里介绍第k大,其实也等价与求第n-k+1小

算法流程大致如下:

随机选择一个数,将他与最后一个元素交换(随机是因为该算法平均为O(n),但如果对于一个排好序的数组最坏会O(n2),与快排是一样的道理,所以随机选择可以最大化保证复杂度,不理解没关系,往下看)

定义两个指针i,j,初始时均在队首

接下来循环遍历直至指针j到达队尾

重复如下操作:

比较指针j所指元素与队尾元素大小,

  若aj <= aend 则交换i,j指针元素,即ai,aj 同时将i , j 分别右移到下一元素

  否则 右移j指针至下一元素

结束后(此时j即为队尾) 交换ai aj

到了这里,ai右边都是比他大的,左边都是比他小的

此时如果i 恰好为第end-k位,则结束,返回

否则视情况递归执行左右区间

最终得到所求第k大数

此题得解

#include<cstdio>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cstring>
#include<string>
#include<ctime>
#include<cstdlib>
using namespace std;
int a[5000005],n,kk;
int quickselect(int l,int r,int k){
    int p = rand() % (r - l + 1) + l;
    swap(a[p],a[r]); 
    int i = l,j = l;
    while(j < r){
        if(a[j] <= a[r])swap(a[i++],a[j]);
        j++;
    }
    swap(a[i],a[j]);
    if(r == k + i - 1)return a[i];
    if(r > k + i -1)return quickselect(i+1,r,k);
    else return quickselect(l,i - 1,k - (r - i + 1));
}
int main(){
    srand(time(NULL));
    scanf("%d%d",&n,&kk);
    for(int i=1;i<=n;++i)scanf("%d",a+i);
    printf("%d",quickselect(1,n,n-kk));//题里最小是第0小
}

 

上一篇:开源OSS.Social微信项目进阶介绍


下一篇:编译运行第一个Java程序——通过示例学习Java编程3