快速排序

快速排序也是基于比较进行的排序,他的原理是将未排序元素根据一个作为基准的“主元”(pviot)分为两个子序列,其中一个子序列的元素均小于主元,另一个均大于主元,然后递归的对这两个子序列进行排序。本质上,快速排序使用的仍然是分治法,将问题的规模不断缩小,然后分别进行处理

我们将比主元大的元素从右向左放置,比主元小的元素从右向左放置,算法步骤如下:

  1. 选择一个主元,并与最后一个元素交换(交换是为了方便的确定扫描区间,Left+1~Right-2)
  2. 设置两个指针Low和High,初值分别指向第一个和倒数第二个元素
  3. Low从左向右扫描,它的左侧全是比他小的元素,High从右向左扫描,它的右侧全是比他大的元素。每当Low遇到比主元大的元素,就停下,High遇到比主元小的元素,也会停下。
  4. 如果Low<High,那么二者指向的元素互换位置
  5. 重复第3、4步,直到Low和High错位(Low>High),将主元和Low指向的元素交换位置,这就完成了一次划分,以主元为边界,左边都比他小,右边都比他大(也就是一次性抵达了该到的位置)
  6. 递归的对主元左侧子序列,右侧子序列用同样方法进行快排

快排的时间复杂度:快排的时间复杂度略显复杂,在最好的情况下,每一次都恰好把序列一分为二,那么整个算法的递归深度就是logN,而在每一层递归上,Low和High都要从各自的起点出发,朝相反方向扫描,直到二者错位为止,也就是整个序列都要进行一次扫描并进行比较,也就是O(N)。那么整个算法在最好情况下,时间复杂度是O(NlogN)

整个算法最坏的情况,是在pviot的选择上出现了问题,比如太大或者太小,会导致分出的两个子集一边很大一边很小,这就失去了分治的意义。所以为了避免这种最坏主元的情况,我们可以设置一些方法来选择主元,比如选择前3个元素中既不是最大也不是最小的那个,这样避免了会选择到最差的那一个

另外一个问题是,由于快速排序使用的是递归实现的方式,待排序序列太小时,递归的副作用就会凸显,效果甚至还不如简单的插入排序,所以我们可以在快排中增加一个规模判定,当子问题缩小到一定地步,就不再递归,而是直接调用插入排序解决问题。

快排为什么快呢?以插入排序为例,我们每一次插入一个元素,此时元素所处的位置都是临时的(最后一次除外),但快排不同,快排一旦选定了pviot,它在整个序列中的位置就唯一确定了。快排的过程也是不断选择pviot的过程,换而言之,就是每一次pviot选择函数的调用,都会唯一的确定一个元素的位置

代码如下:

ElementType Median3(ElementType A[],int Left,int Right)//pivot选择 
{
	int Center=(Left+Right);
	if(A[Left]>A[Center])
	swap(A[Left],A[Center]);
	if(A[Left]>A[Right])
	swap(A[Left],A[Right]);
	if(A[Center]>A[Right])
	swap(A[Center],A[Right]);//此时A[Left]<=A[Center]<=A[Right]; 
	swap(A[Left],A[Right-1]);//把基准pivot换到右边
	return A[Right-1];//返回基准pivot 
}

void Qsort(ElementType A[],int Left,int Right)
{
	int Pivot,Cutoff,Low,High;
	if(Cutoff<=Right-Left)//如果元素足够多,使用快排 
	{
		Pivot=Median3(A,Left,Right);
		Low=Left;
		High=Right-1;
		//仔细观察这个赋值方式和循环,会发现函数只扫描了Left-1到Right-2
		//事实上,Left已经在选Pivot的过程中安排明白了,Right-1则是pivot 
		while(1)
		{
			while(A[++Low]<Pivot);
			while(A[--High]>Pivot);
			if(Low<High)
			//两个指针从左右端开始,向中间扫描
			//当遇到一个大于pivot,一个小于Pivot的元素的时候,交换二者的位置 
			swap(A[Low],A[High]);
			else
			break;
		}//这一趟实际上确定了pivot的最终位置,并放到了A[low]上 
		swap(A[Low],A[Right-1]);
		Qsort(A,Left,Low-1);
		Qsort(A,Low+1,Right);//递归解决左右 
	}
	else InsertionSort(A+Left,Right-Left+1);//元素较少,直接使用插入排序 
}

void QuickSort(ElementType A[],int N)//统一接口 
{
	Qsort(A,0,N-1);
}

void InsertionSort(int A[],int N)
{
	int P,i;
	ElementType Tmp;
	for(P=1;P<N;P++)
	{
		Tmp=A[P];
		for(i=P;i>0&&A[i-1]>Tmp;i--)
			A[i]=A[i-1];
		A[i]=Tmp;
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值