快速排序

 1.算法描述
快速排序是一种基于分治技术的排序算法。在一个给定的数列中,选择一个数作为分区的依据进行排序,使得数的左边都小于该数,数的右边都大于该数,然后将该数的左边和右边分别作为一个数列进行排序,一直重复以上操作,直到分区里只有一个数字为止。
上面所说的是快速排序的基本特点,可以简单总结为:确定分区依据,左小右大(或左大右小),划分子区间,重复以上操作,直到子区间只有一个数字。
在具体的实现中,快速排序的实现不止一种。比如如何确定分区的依据,如何排序使得分区数字左边所有数字都小于该数字,右边都大于该数字,这可以有很多不同的实现方式。下面讲到的是一种基于两次扫面子数列的方法。


2.实例
使用快速排序将下列数列按升序排序
6     4     1     19     10     5

第一次扫描,选择6作为分区依据,第一次扫描结束后6的左边都小于6,右边都大于6,因此从左边开始扫描时,如果这个数比6小,则继续扫描,直到找到一个数字比6大,左边停止扫描,右边开始扫描,如果这个数字比6大,则继续扫描,直到找到一个数字比6小,右边停止扫描,然后左右扫描停止的位置互换,然后重新开始左右扫描,直到坐标的索引值大于右边的索引值。根据这些分析,我们开始排序。

第一次扫描
设该数列为int number[6],坐标索引为left,右边索引为right
left=1左边开始扫描,4<6继续扫描;left=2, 1<6继续扫描;left=3,19>6,左边扫描停止;
right=5右边开始扫描,5<6右边扫描停止;左右互换数据后数列为:
6     4     1     5     10     19
left<right,扫描继续。left=4左边开始扫描,10>6坐标扫描停止;
right=4右边开始扫描,10>6,扫描继续;right=3,5<6,右边扫描停止;左右互换数据后数列为:
6     4     1     10     5     19
left>right,第一次扫描技术。扫描结束的条件是left>=right,因此当left>right时,最后一次交换回是错误的,因此扫描结束后,应该还原最后一次互换。left和right再次互换,数列如下:
6     4     1    5     10     19
将6和right的值互换,数列如下:
5     4     1     6     10     19
这样第一次扫描才正真的结束,6的左边的数都小于6,6右边的数都大于6


第二次扫描
分别将number[0..right-1]和number[right+1,5]看做两个数列,进行步骤一的操作。
5     4     1              6               10     19
设number[0..right-1]为number_left[0..right-1],left1=1开始左边扫描,4<5,继续扫描;left1=2,1<5,继续扫描,但是number_left只有三个数字,因此左边扫描结束;
right1=2开始右边扫描,1<5,右边扫描结束。互换left1和right1,数列如下:
5     4     1              6                10       19
left1==right1,number_left的扫描结束,left1和right1互换,5和right1互换位置,数列如下:
1     4     5              6                10       19

第三次扫描
分别对number_left[0..right1-1]和number_left[right1+1..right-1]排序,因为right1+1=3,right-1=2,所以number_left[right1+1..right-1]不存在。
设number_left[0..right1-1]为number_left_left[0..right1-1],left11=1左边开始扫描,4>1左边扫描结束;
right11=1右边扫描开始,4>1扫描继续;right11=0,1==1,右边扫描结束;left11和right11互换后数列如下:
4     1                    5                              6                              10       19
left11>right11,number_left_left扫描结束,left11和right11互换,1和right11互换后数列如下:
1     4                    5                              6                               10      19
到这里6之前的排序都结束了,然后再对number_right也使用上面描述的方法进行排序,这里就不在重复描述了。



3.代码
void quick_sort::quick_sort_with_array(int nums[], int begin_index, int end_index)
{
	
	int index = 0;
	if(begin_index<end_index)
	{
		index = partition(nums, begin_index, end_index);

		quick_sort_with_array(nums, begin_index, index-1);
	    quick_sort_with_array(nums, index+1,end_index);
	}	
	
}
int quick_sort::partition(int nums[],int begin_index, int end_index)
{
	int left=begin_index;
	int right = end_index;
	int number = nums[begin_index];
	while(left<right)
	{
		while(nums[left]<=number && left<end_index)  left++;
		while(nums[right]>=number && right>0) right--;

		swap(nums[left],nums[right]);
	}

	swap(nums[left],nums[right]);
	swap(nums[begin_index], nums[right]);

	return right;
}
void quick_sort::swap(int& a, int& b)
{
	int temp = a;
	a        = b;
	b        = temp;
}


4.算法分析
在快速排序中,比较和位置替换依然是主要操作。先来看一下比较操作。从上面的代码中我们可以看出,程序采用从左右两端扫描数列的方式,每次扫描结束后,一个数字就会被放到合适的位置,及排除出待排序序列,直到最后所有子数字的个数都是1.
设对一个长度为n的数列进行升序排序,如果给定的已经是一个按升序(或降序)排序的数列,这种情况下比较的次数最多,比较次数为:
                                                             C(n)=(n+1)+n+(n-1)+...+3=(n+1)*(n+2)/2-3
上面讨论的是最坏情况,如果排序的时候,分裂点都是中心位置,则为最优情况,比较次数为:
                                                              C(n)=2*C(n/2)+n         (n>1,C(1)=0,C(2)=2)
设n=2^k,根据上面的公式可推算出:




在平时使用时,上面讨论的两种情况是不经常遇到的,下面我们来讨论一下快速排序在平均情况下的效率:
设有一个长度为n的数列,快速排序的平均键值比较次数为 Cavg(n),假设分区的分裂点s(0<= s<=n-1)位于每个位置的概率是1/n,可以得出下面的公式:
                                         
根据公式推算得:




因为1/(n+1)+1/n+1/(n-1)+...1/2为发散序列,无法算出精确值,只能是大约值,网上找到的是约等于lnn,但证明过程没有找到,证明就暂时到这里,以后再做修改吧。因此快排的平均复杂度为
                                                                
从上面的计算结果可以看出,快排在平均情况仅比最优情况多执行了38%的操作。















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值