【Jason's_Knowledge】算法竞赛中的快速排序及其应用

算法竞赛中的快速排序及其应用

 

本文适合有算法功底的算法竞赛爱好者,是一片总结助记性质的文章。

由于鄙人才疏学浅,如有错误感谢各位前辈批评指正。

 

一、快速排序简述

    快速排序使用分治法策略来把一个序列分为两个子序列。    步骤为:

1.从数列中挑出一个元素,称为"基准"

2.重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准的后面(相同的数可以到任一边)。在这个分割结束之后,该基准就处于数列的中间位置。这个称为分割操作。 3.递归地把小于基准值元素的子数列和大于基准值元素的子数列合并    递回的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递回下去,但是这个演算法总会结束,因为在每次的迭代中,它至少会把一个元素摆到它最后的位置去。

 

    由于在算法竞赛当中允许使用标准模板库(STL),我们通常使用sort函数来实现快速排序,而且sort函数有时也有很高的效率。

 

二、快速选择问题

输入N个整数和一个正整数K(1<=K<=N),输出这些整数从小到大排序后的第K个,N<=10^7。

这个题,最粗暴的想法就是将整个数列快速排序,然后取出第K-1个元素(C语言中下标从0开始)。但是,这个算法并非完全正确,O(NlogN)的时间复杂度应对这个数据规模显得有些吃力。

有没有时间复杂度为O(N)的算法呢?

当然,我们可以在快速排序划分出两个子序列结束之后(比如此时基准左侧是A[l..x],右侧是A[x+1..r]),那么我们可以根据左边元素个数x-l+1和K的大小关系,只在左侧或者只在右侧递归的求解。

这种方法在期望以意义下时间复杂度为O(N)。

 

附代码如下:(目前代码的正确性还有待考证,日后刷几道题再来看看吧)

#include<ctime>
#include<cstdio> 
#include<cstring>
#include<cstdlib>

using namespace std;

#define MAXN (10000000+5)
#define random(x) (rand()%x)

int a[MAXN];
int n,k;

int swap(int &x,int &y){
	int i=x;x=y;y=i;
}

int sel(int l,int r,int k){
	int i=l,j=r,mid=a[l+random(r-l+1)];
	do{
		while(a[i]<mid)i++;
		while(mid<a[j])j--;
		if(i<=j){
			swap(a[i],a[j]);
			i++;
			j--;
		}
	}while(i<=j);
	
	if(l<j&&k<=j)return sel(l,j,k);
	if(i<r&&i<=k)return sel(i,r,k);
	return a[k];
}

int main(){
	srand((int)time(0));
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	printf("%d\n",sel(0,n-1,k-1));
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值