求第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
本人能力有限,如有错误,还请批评指正