快速排序区间K大数查询

题目
问题描述
给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。
输入格式
第一行包含一个数n,表示序列长度。
第二行包含n个正整数,表示给定的序列。
第三个包含一个正整数m,表示询问个数。
接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。
输出格式
总共输出m行,每行一个数,表示询问的答案。
样例输入
5
1 2 3 4 5
2
1 5 2
2 3 2
样例输出
4
2
数据规模与约定
对于30%的数据,n,m<=100;
对于100%的数据,n,m<=1000;
保证k<=(r-l+1),序列中的数<=106。
题解
快排的原理:利用快速排序将无序序列转换为有序序列的过程中,经过多次的划分;(这里假定进行降序排列)在每一次的划分中,在序列中找到一个枢轴(pivot),划分的结果是 pivot 左边的数均比它大,右边的数均比它小;然后对子序列进行同样的操作,递归进行、直到所有子序列有序为止。
改造:我们的目标是找到第K大的数,若在一次划分之后、pivot左边的数(加上它本身)共有K个,那么pivot位置上的数就是我们要找的第K大的数;其一:我们不必要求前K-1大的数有序,所以不必继续进行递归划分;其二:每一次的划分都将原序列分为左、右两个子序列,快排算法需要对这两个子序列都继续进行递归的划分,而在这里我们只需要根据情况(具体看算法步骤)选择一个子序列继续划分即可;因此这种求第K大的数算法的时间复杂度小于快速排序的复杂度,为O(n*logK)
算法步骤:经过一次划分(partition)之后,枢轴pivot将原序列划分为两个部分:S和T [即原序列变为(S T)、pivot包含在子序列S中、注意使用降序排序],会出现下列三种情况:
1.子序列S中有K个数,此时pivot位置即为第K大的数,算法结束
2.子序列S中的数字个数小于K,假设个数为L,则需要子序列T中继续递归划分出来前(K-L)个数
3.子序列S中的数字个数大于K,则需要子序列S中继续递归划分出来前K个数
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std; 
/*快速排序的一次划分*/
int partition(int a[], int low, int high) {
	int i = low, j = high, x = a[low];
	while(i<j) {
		while(i<j && a[j]<=x) { j--; }
		a[i] = a[j];
 
		while(i<j && a[i]>=x) { i++; }
		a[j] = a[i];
	}
	a[i] = x;
	return i;
} 
/* 降序排序,当一次划分之后、pivot左边的数(包括它本身)恰为k个时,停止划分*/
void find_kmax(int a[], int low, int high, int k) {
   	if (low < high) {
    	        int poi = partition(a, low, high);// 得到一次划分之后 pivot的位置
                int len = poi - low + 1;// 计算子序列S的长度
		if (len < k) {// 情况2
			find_kmax(a, poi + 1, high, k - len);
		} else if (len > k) {// 情况3
			find_kmax(a, low, poi - 1, k);
		}
     }
}
int main() {
	int arr[] = {23, 50, 500, 4, 100, 300, 200, 99, 400};
	int len = sizeof(arr) / sizeof(int);// 求数组长度
 
	int l, r, k;// 区间为[l, r],从1开始
        scanf("%d %d %d", &l, &r, &k);
 
        int a[100];
        memcpy(a, arr, sizeof(arr));// 将arr数组的元素copy到a数组中,这么做是为了避免破坏原数组、能够进行多次不同区间的查询
        find_kmax(a, l-1, r-1, k);// 注意区间下标与数组下标的转换
        printf("%d\n", a[l-1 + k-1]);// 注意第k大的数在数组a中的位置!
 
	return 0;
}

题解二:学了快速排序后的另外一种解法:

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e6+10;
int n,k,q[N];
void quick_sort(int q[],int l,int r){
    if(l>=r)return;
    int i=l-1,j=r+1;
    int  x=q[l+r>>1];
    while(i<j){
        do i++;while(q[i]<x);
        do j--;while(q[j]>x);
        if(i<j) swap(q[i],q[j]);
    }
    quick_sort(q,l,j);
    quick_sort(q,j+1,r);
}
int main(){
    scanf("%d%d",&n,&k);
    if(n>=1&&n<1e9+10){
         for(int i=0;i<n;i++) scanf("%d",&q[i]);
    
    quick_sort(q,0,n-1);
    for(int i=0;i<n;i++){
    		if(k==i+1) printf("%d",q[n-i-1]);
    }

    }
   
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值