快速排序(Quick Sort)

简述

快速排序算法最早由图灵奖获得者Tony Hoare设计,他在形式化方法理论以及ALGOL60编程语言的发明中都有卓越的贡献,是上世纪最伟大的计算机科学家之一。而接下来的快速排序算法,被列为20世纪十大算法之一。
快速排序与我们认为比较慢的冒泡排序一样都是交换类排序,只不过它的实现,增大了记录的比较与移动的距离,不是相邻比较交换的冒泡形式。

基本思想

通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

枢纽元(pivot)选取

这里主要介绍三数中值分割法(Median-of-Three Partitioning)

pivot的最好选择自然是数组的中值,但显然要想算出中值开销较大。所以一般的做法是使用左端,右端和中心位置上的三个元素的中值作为pivot。

分割策略与大致实现思路

该方法的第一步将pivot与最后的元素交换使得pivot离开要被分割的数据段,i从第一个元素开始j从倒数第二个元素开始(实际操作中并非如此),i往右移动直到遇见大于pivot的元素,停止;j往左移动直到遇见小于pivot的元素,停止。

如果i<j交换i与j对应的元素。

一轮结束,如果i仍然小于j,继续重复上述交换流程(i与j位置不重置),直到i与j交错,即i>=j.

此刻i左边的数据段每个元素都小于pivot,i右边的数据段每个元素都大于pivot,此刻交换pivot(本来在最后一个元素的位置)与i对应元素的位置。

对于与pivot相等的元素的处理

对于这种特殊情况可以考虑某种极端情况——比如所有元素值相等。

如果i与j遇到与pivot相等的元素停下,即使会发生多次无意义的交换,但其正面效果是i和j将在中间交错,因此这种分割建立了两个几乎相等的子数组。此时的总的运行时间是O(N logN);

但是如果i与j遇到与pivot相等的元素不停下,就会导致i、j越界,如果想办法加入限制越界的程序,i最后仍会到达倒数第二个位置(倒数第一个是pivot本身,此时所有元素值相同),交换以后会产生非常不均衡的子数组,运行时间会变成O(N^2);

明显前者的方法更好。

小数组

对于很小的数组,快速排序不如插入排序好。一种好的截止范围(cutoff range)是N=10,>N的数组就用快速排序,<N的数组就用插入排序。

实际实现例程

typedef int ElementType

筛选pivot
int Median3(int A[],int Left,int Right)
{
	int Center=(Left+Right)/2;
	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]);
	/*Invariant:A[Left]<=A[Center]<=A[Right]*/
	swap(A[Center],A[Right-1]);
	return A[Right-1]; 
}

这样的实现不仅能简便选出pivot,通过对三个关键位置的排序还有额外的好处。

1、A[left]与A[Right]都已经处于合适的位置。
2、例程中将pivot置于A[Right-1]的位置,所以i与j分别从Left+1与Right-2出发。而A[Left]比pivot小,j遇到其会停止,不会越界;同理i遇到A[Right-1]会停止,不会越界。

核心

void Qsort(int A[],int Left,int Right)
{
	int i,j;
	int Pivot;
	if(Left+Cutoff<=Right)
    {
    	Pivot=Median3(A,Left,Right);
    	i=Left,j=Right-1;
    	for(;;)
    	{
    		while(A[++i]<Pivot)	 ;
			while(A[--j]>Pivot)  ;
			if(i<j)
			   swap(A[i],A[j]);
			else
			   break;
		}
		swap(A[i],A[Right-1]);
		Qsort(A,Left,i-1);
		Qsort(A,i+1,Right);
	}
	else
	    InsertionSort();//插入排序 
}

注意其中的for循环,自增自减这里是++i与–j,因为初始化是将i,j赋Left与Right-1,而得从Left-1与Right-2开始移动。

同时不可将自增自减运算放于while语句的循环体部分,因为如果A[i]==A[j]==Pivot时,将会死循环。


参考书籍——《深入浅出程序设计竞赛(基础篇)》,《数据结构与算法分析》(Mark Allen Weiss),《大话数据结构》;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值