《算法导论》学习笔记(2)——快速排序

         快速排序,是一种基于分治思想的排序算法。分治,简单来说就是“大事化小”,从而达到最基本的问题,然后再将其合一。

         在快速排序算法中,每次需要把一个数组A[p...r]分为三部分:

A[p...q-1]   A[q]  A[q+1...r]

而对两个子数组:A[p...q-1]和A[q+1...r]的要求却很少:只需要满足A[p...q-1]中的每个元素都小于等于A[q],A[q+1...r] 中的每个元素都大于等于A[q]即可。每个子数组可以为空,内部也不需要有序。之后,再将两个子数组进行这样的划分,如此递归下去,最终得到的就是一个有序的数组。


         该算法中,最重要的步骤就是数组的划分。用函数 partition 来实现:

         传入参数:待排序数组A、当前子数组的首尾位置p和r

         返回值:中间元素位置q

         开始时,选取当前数组的最后一个元素作为主元(pivot element),当然,这样选择完全是个人定的,其实完全可以选择该数组中的任意一个元素。选定了主元之后,设定一个游标,然后依次遍历整个数组,每次比较当前元素和主元。把小于主元的元素放在游标左边,大于等于主元的元素放在右边。最后再把主元放在游标的位置即可,而且游标的下一个位置就是要返回的中间元素位置q。

         如图所示,在给定的数组A[p...r]中,主元是最后一个位置的元素A[r]=x,游标是i,j表示当前正遍历到的位置。我们可以看到,从p到i之间是小于等于x的元素,从i+1到j-1之间是大于x的元素。

         若当遍历到的下一个元素小于等于x时,游标i右移一位,即腾出一个位置给该元素,当然j也要右移。若当遍历到的下一个元素大于x时就不需要移动游标,只需j右移即可。

 

         算法代码如下:

#include <iostream>
using namespace std;

void swap( int *a, int *b )
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

int partition( int *A, int front, int behind )
{
	int i = front - 1;
	for( int j=front; j<behind; j++ )
	{
		if( A[j] <= A[behind] )
		{
			i++;
			swap( &A[i], &A[j] );
		}
	}
	swap( &A[i+1], &A[behind] );
	return i + 1;
}

void QuickSort( int *A, int front, int behind )
{
	int mid;
	if( front < behind )
	{
		mid = partition( A, front, behind );
		QuickSort( A, front, mid-1 );
		QuickSort( A, mid+1, behind );
	}
}

int main()
{
	int A[10] = {2,5,3,1,0,9,8,6,7,4};
	QuickSort( A, 0, 9 );
	for( int i=0; i<10; i++ )
		cout << i[A] << " ";
	cout << endl;
	return 0;
}

        关于快速排序的性能也是值得分析的。快速排序的运行时间依赖于划分是否平衡,就是划分的两个子数组中元素的个数是否相近。很容易直观的感觉到,最好的情况就是每次都是均分,这样的时间复杂度就是O(nlgn)。而最坏的情况就是每次划分的子数组中都有一个为空,最典型的就是把一个升序的数组排成降序。这样的话每次划分都只能排好一个元素,所以此时快速排序就退化为插入排序,时间复杂度就是O(n²)。

         一般来说,如果把快速排序的划分平均来看,可以得到一棵递归树。如果我们假设划分成的两个子数组中元素个数的比例为一个常数,比如1:1,或者99:1,那么递归树的深度就为Θ(lgn),而其中的每一层的时间代价都是O(n),因此算法的运行时间总是O(nlgn)。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值