第k小数

alt

解题思路:

快排:随机选一个数为基准(一般选中间的数)将大于它的放右边,小的放左边。(要把中间数的值存下来,因为仅靠mid来锁定这个基准数,是不够的,他的位置会变。)

  • 用快排来解决问题,不同的是对于没用到的一边不用排序。

解题代码:

#include<bits/stdc++.h>
using namespace std;
int a[5000010];
inline int read(){//题目给的输入函数不用管
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}
int find(int l,int r,int k){
	if(l==r) return a[l]; //缩小到最小的出口时,运气差!
	int i=l,j=r;
	int mid=(l+r)/2;
	int x=a[mid];//将基准值保存下来
	while(i<=j){//直到两个指针i,j错过后停止
		while(a[i]<x) i++;//当顺序是对的就往后走
		while(a[j]>x) j--;//当顺序是对的就往前走
		if(i<=j){//已经包含a[j]>=x>=a[i]的意思,存在i>j的情况,所以要再次判断i<=j
			swap(a[i],a[j]);
			i++;j--;
		}
	}
	if(j>=k) return find(l,j,k);//判断k在什么范围,继续在那个范围找,缩小范围
	if(i<=k) return find(i,r,k);
	return a[k];//如果刚好是的话就输出,i,j有两种位置可能:中间夹一个或相邻
}
int main(){
	int T;
	T=read();
	while(T--){
		int n,k,i;
		n=read();k=read();
		for(i=0;i<n;i++){
			a[i]=read();
		}
		int ans=find(0,n-1,k-1);
		printf("%d\n",ans);
	}
	return 0;
}