题目描述
如题所述,找到n个数中第K小的数字。
但是不同的是时间复杂度要求为O(n),也就是说基本上所有的排序算法都不能用了。
这里适合的算法是分治法,也就是使用快速排序。因为这道题的一个特点是只需要得到第k小的数字,而并没有说要对所有元素进行排序。如果我们把所有小于某个元素的元素都置于其左侧,且正好有k-1个这样的元素,那么这个元素自然地就是第k个元素。
代码如下:
#include<bits/stdc++.h>
using namespace std;
long long a[5000010];//一定要设全局变量
//经过实验,不设全局变量而把数组作为参数传入函数也能AC,并不是一定要设全局变量。
inline int read(){ //快读
//比cin和cout使用的时间更短
char ch=getchar();
int x=0,f=1;
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while('0'<=ch&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
} return x*f;
}
int quicksort(int left,int right)
{
//快速排序中选择的枢轴可以有很多种策略,这里是直接用a[left]作为枢轴
//这个函数执行完成后,数组a中left-right的部分会以a[left]为界
int mid=a[left];//选取枢轴
while (left<right)
{
while (left<right&&mid<=a[right])
right--;
a[left] = a[right];
//从右往左循环查找小于枢轴的元素,如果发现就将其提到前面left处
//不用担心,a[left]已经存储在变量mid里面了
while (left<right&&a[left]<=mid)
left++;
a[right] = a[left];
//从左往右循环,发现比枢轴大的元素就放到后面去
}//退出循环时一定有left==right,而且左右两侧的元素分别小于和大于枢轴
a[left]=mid;
return left;
//返回枢轴在一轮排序后所在下标
}
int find(int left, int right, int k)
{
//注意,这个函数的作用是在left到right的子数组中寻找整体数组中第k小的数字
//这个k是目标在整体数组中的位置,而不是子数组中
int tem=quicksort(left,right);
if(k==tem)
printf("%d",a[k]);
//这里是A[k]而不是A[tem]的原因是数组a是一直被修改的
//所以当k == tem时,A[k]自然而然的就是第k小的元素
else if(k-1<tem)
find(left,tem-1,k);
else
find(tem+1,right,k);
return 0;//注意这里
}
int main()
{
int n,k;
n=read();
k=read();
for(int i=0;i<n;i++)
a[i]=read();
find(0,n-1,k);
return 0;
}
注意!!
如果写的函数返回值类型不是void,就必须在函数里写上return语句,就算你并不需要任何返回值,否则就会产生runtimeerror。
另外,在某些时间复杂度要求苛刻的题目中,cin和cout是不能用的,需要用到快读函数来节省时间。