求数列第K大的数--ACdream 1099

求第K大的数,经常会想到主席树,但是主席树代码复杂,操作复杂,如果说单纯的求数列第K大的数,那么就显得优点大材小用了,就像ACdream 1099 这道题目,就是求数列中第K大的数,而且如果有重复的话也算在第K大之内,例如:如果数列是{3,2,2,1},要求第一大的数,显然就是3,第二大的数也显然就是2,这都没有疑问,但是第三大的数是1还是2呢?对于这道题而言,第三大的是2,第四大的是1
看到这道题,很多人会直接想到sort一下,然后找到第K个数就可以了,但是sort是tle的,实际上他考察的是分治思想,自己写一个排序规则,但是要进行一些优化
首先解读一下分治、排序、优化代码

//求数列中第K大的数字
int sort_up_kth(int left,int right,int n,int k)
{//left和right分别为左右区间边界,n为序列长度
    int l=left;
    int r=right;
    //没有直接使用left和right执行操作是因为再接下来处理的时候
    //可能会对这两个值进行修改,但最后可能会用到这两个值
    //所以只能定义两个变量将其赋值修改
    if(l<r){//符合要求
       int x=a[l];
       while(l<r){
        while(l<r&&a[r]<x) r--;
        //从右端点向左找到第一个大于等于当前值的数字
        if(l<r) {
            a[l]=a[r];
            l++;
        }
        while(l<r&&a[l]>=x) l++;
        //从左端向右端找到第一个小于当前值的数字
        if(l<r) {
            a[r]=a[l];
            r--;
        }
       }
       a[l]=x;
       if(l==k) return 1;//找到第K大的数字了,直接结束,节省时间
       if(l>k) sort_up_kth(left,l-1,n,k);
       //分治思想
       else sort_up_kth(l+1,right,n,k);
    }
}
//实际上在这个数列中,只排好了前K大的数字,其他数字都是在后面乱序的

这就是求解序列第K大的数字,简单理解一下原理(我写的并不详细,还请见谅),就可以轻松的推导出第K小的数字的求解方法了

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
int a[5000005];
int sort_up_kth(int left,int right,int n,int k)
{//第K大数字
    int l=left;
    int r=right;
    if(l<r){
       int x=a[l];
       while(l<r){
        while(l<r&&a[r]<x) r--;
        if(l<r) {
            a[l]=a[r];
            l++;
        }
        while(l<r&&a[l]>=x) l++;
        if(l<r) {
            a[r]=a[l];
            r--;
        }
       }
       a[l]=x;
       if(l==k) return 1;
       if(l>k) sort_up_kth(left,l-1,n,k);
       else sort_up_kth(l+1,right,n,k);
    }
}
int sort_down_kth(int left,int right,int n,int k)
{//第K小的数字
    int l=left;
    int r=right;
    if(l<r){
        int x=a[l];
        while(l<r){
            while(l<r&&a[r]>x) r--;
            if(l<r){
                a[l]=a[r];
                l++;
            }
            while(l<r&&a[l]<=x) l++;
            if(l<r){
                a[r]=a[l];
                r--;
            }
        }
        a[l]=x;
        if(l==k) return 1;
        if(l>k) sort_down_kth(left,l-1,n,k);
        else sort_down_kth(l+1,right,n,k);
    }
}
int main(){
    int n,k;
    while(~scanf("%d%d",&n,&k)){
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        sort_up_kth(1,n,n,k);
        printf("第%d大的数字是%d\n",k,a[k]);
        sort_down_kth(1,n,n,k);
        printf("第%d小的数字是%d\n",k,a[k]);
    }
return 0;}
///6 3
///1 2 3 4 5 6
///第3大的数字是4
///第3小的数字是3
///6 4
///1 2 3 4 4 4
///第4大的数字是3
///第4小的数字是4
///6 4
///1 3 2 1 2 3
///第4大的数字是2
///第4小的数字是2

本人能力有限,如有错误,还请批评指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值