c++里面有模板,这里不赘述。主要是c语言实现.
主要思想是递归+快排
1.用一个数组,进行原址选择,缺点是只能进行一次。
将一个数组以某一个元素a作为基准(这里选取最后一个),将这个数组划分,使得左边的元素都比a大,右边的都比a小。当然如果是求第K小,只需对k进行转换即可。
#include<stdio.h>
void swap(int *a,int *b)
{
int t=*a;
*a=*b;
*b=t;
}
*num数组名,k第几大,n元素个数,p,r分别是数组下标
int k_max(int *num,int k,int n,int p,int r)
{
int num1,num2;//统计左边、右边元素各有多少个
num1=num2=0;
int i=p-1;
int key=num[r];//基准
//下面是快排思想
if(p<r)
{
for(int j=p;j<r;j++)
{
if(num[j]<key)
{
i++;
num1++;
swap(&num[i],&num[j]);
}
}
swap(&num[r],&num[i+1]);
}
else return num[r];//终止条件
num2=n-num1;
if(num1>=k)
{
//第k大的元素在左边元素里
return k_max(num,k,num1,p,p+num1-1);
}
else
{
//在右边元素里
return k_max(num,k-num1,num2,p+num1,r);
}
}
int main(void)
{
int n;
scanf("%d",&n);
int num[100001]={0};
for(int i=1;i<=n;i++)
{
scanf("%d",num+i);
}
int i,j,k;//分别为数组下标,第几大
scanf("%d%d%d",&i,&j,&k);
printf("%d\n",k_max(num,k,j-i+1,i,j));
}
2.再开两个数组,优点是可以多次进行。
#include<stdio.h>
void swap(int *a,int *b)
{
int t=*a;
*a=*b;
*b=t;
}
int sav1[100001];
int sav2[100001];
int k_max(int *num,int k,int n,int p,int r)
{
int i,l;
i=l=0;
int key=num[r];
if(p<r)
{
for(int j=p;j<=r;j++)
{
if(num[j]<key)
{
sav2[++i]=num[j];
}
else if(num[j]>key)
{
sav1[++l]=num[j];
}
}
}
else return num[r];
if(l>=k)
{
return k_max(sav1,k,l,1,l);
}
else if(l<k-1)
{
return k_max(sav2,k-l-1,i,1,i);
}
else return key;
}
int main(void)
{
int n,m;
scanf("%d%d",&n,&m);
int num[100001]={0};
for(int i=1;i<=n;i++)
{
scanf("%d",num+i);
}
while(m--)//指令条数
{
int i,j,k;
scanf("%d%d%d",&i,&j,&k);
printf("%d\n",k_max(num,k,j-i+1,i,j));当所求为第k小时,只需k=j-i-k+2;
}
}
注意二者的区别,在1中我们把基准元素也进行排序,而2中不对基准元素进行排序,所以2中的特别判断key是不是所求。
如果我们仍然采用1中递归结束的标志,那么会出现死循环。