快速排序
分而治之方法还可以用于实现另一种完全不同的排序方法,这种排序法称为快速排序(quick sort)。在这种方法 中, n 个元素被分成三段(组):左段l e f t,右段r i g h t和中段m i d d l e。中段仅包含一个元素。左段中各元素都小于 等于中段元素,右段中各元素都大于等于中段元素。因此l e f t和r i g h t中的元素可以独立排序,并且不必对l e f t和 r i g h t的排序结果进行合并。m i d d l e中的元素被称为支点( p i v o t )。图1 4 - 9中给出了快速排序的伪代 码。
/ /使用快速排序方法对a[ 0 :n- 1 ]排序
从a[ 0 :n- 1 ]中选择一个元素作为m i d d l e,该元素为支点
把余下的元素分割为两段left 和r i g h t,使得l e f t中的元素都小于等于支点,而right 中的元素都大于等于支点
递归地使用快速排序方法对left 进行排序
递归地使用快速排序方法对right 进行排序
所得结果为l e f t + m i d d l e + r i g h t
图14-9 快速排序的伪代码
考 察元素序列[ 4 , 8 , 3 , 7 , 1 , 5 , 6 , 2 ]。假设选择元素6作为支点,则6位于m i d d l e;4,3, 1,5,2位于l e f t;8,7位于r i g h t。当left 排好序后,所得结果为1,2,3,4,5;当r i g h t排好序后,所 得结果为7,8。把right 中的元素放在支点元素之后, l e f t中的元素放在支点元素之前,即可得到最终的结果[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]。
把元素序列划分为l e f t、m i d d l e和r i g h t可以就地进行(见程序1 4 - 6)。在程序1 4 - 6中,支点总是取位置1中的元素。也可以采用其他选择方式来提高排序性能,本章稍后部分将给出这样一种选择。
程序14-6 快速排序
template
void QuickSort(T*a, int n)
{// 对a[0:n-1] 进行快速排序
致相同,快速排序的复杂性为(nl o gn)。令人吃惊的是,快速排序的平均复杂性也是(nl o gn)。
定理2-1 快速排序的平均复杂性为(nl o gn)。
证 明用t (n) 代表对含有n 个元素的数组进行排序的平均时间。当n≤1时,t (n)≤d,d为某一常数。当n <1时,用s 表示左段所含元 素的个数。由于在中段中有一个支点元素,因此右段中元素的个数为n-s- 1。所以左段和右段的平均排序时间分别为t (s), t (n-s- 1 )。分割数组中元素所需要的时间用cn 表示,其中c 是一个常数。因为s 有同等机会取0 ~n- 1中的任何一个值.
如 对(2 - 8)式中的n 使用归纳法,可得到t (n)≤kn l o ge n,其中n> 1且k=2(c+d),e~2 . 7 1 8为自 然对数的基底。在归纳开始时首先验证n= 2时公式的正确性。根据公式( 1 4 - 8),可以得到t( 2 )≤2c+ 2d≤k nl o ge 2。在归纳假设部分,假定t(n)≤kn l o ge n(当2≤n<m 时,m 是任意一个比2大的整数=.
图1 4 - 1 0对本书中所讨论的算法在平均条件下和最坏条件下的复杂性进行了比较。
方法最坏复杂性平均复杂性
冒泡排序n2 n2
计数排序n2 n2
插入排序n2 n2
选择排序n2 n2
堆排序nl o gn nl o gn
归并排序nl o gn nl o gn
快速排序n2 nl o gn
图14-10 各种排序算法的比较
中 值快速排序( median-of-three quick sort)是程序1 4 - 6的一种变化,这种算法有更好的平均性能。注意到在程序 1 4 - 6中总是选择a [ 1 ]做为支点,而在这种快速排序算法中,可以不必使用a [ 1 ]做为支点,而是取{a[1],a[(1+r) /2],a[r]} 中大小居中的那个元素作为支点。例如,假如有三个元素,大小分别为5,9,7,那么取7为支点。为了实现中值快速排序算法,一种最简 单的方式就是首先选出中值元素并与a[1] 进行交换,然后利用程序1 4 - 6完成排序。如果a [ r ]是被选出的中值元素,那么将a[1] 与 a[r] 进行交换,然后将a [ 1 ](即原来的a [ r ])赋值给程序1 4 - 6中的变量p i v o t,之后继续执行程序1 4 - 6中的其余代码。
图2 - 11中分别给出了根据实验所得到的归并排序、堆排序、插入排序、快速排序的平均时间。对于每一个不同 的n, 都随机产生了至少1 0 0组整数。随机整数的产生是通过反复调用s t d l i b . h库中的 r a n d o m函数来实现的。如果对一组整数进行排序的时间少于1 0个时钟滴答,则继续对其他组整数进行排序,直到所用的时间不低于1 0个时 钟滴答。在图2 - 11中的数据包含产生随机整数的时间。对于每一个n,在各种排序法中用于产生随机整数及其他开销的时间是相同的。因此,图2 - 11中的数据对于比较各种排序算法是很有用的。
对于足够大的n,快速排序算法要比其他算法效率更高。从图中可以看到快速排序曲线 与插入排序曲线的交点横坐标比2 0略小,可通过实验来确定这个交点横坐标的精确值。可以分别用n = 1 5 , 1 6 , 1 7 , 1 8 , 1 9进行实验,以寻找精确的交点。令精确的交点横坐标为nBr e a k。当n≤nBreak 时,插入排序的平均性能最佳。当n>nBreak 时,快速排序性能最佳。当n>nBreak 时,把插入排序与快速排序组合为一个排序函数,可以提高快速排序的性能,实现方法是把程序1 4 - 6中的 以下语句:
if(l >= r)r e t u r n ;
替换为
if (r-1
这 里I n s e r t i o n S o r t ( a , l , r )用来对a [ 1 : r ]进行插入排序。测量修改后的快速排序算 法的性能留作练习(练习2 0)。用更小的值替换n B r e a k有可能使性能进一步提高(见练习2 0)。
大多数实验表明,当n>c时(c为某一常数),在最坏情况下归并排序的性能也是最佳的。而当n≤c时,在最坏情况下插入排序的性能最佳。通过将插入排序与归并排序混合使用,可以提高归并排序的性能
**********************************************
转自:http://blog.csdn.net/ehxee/archive/2004/09/10/100460.aspx
**********************************************
看这个例子就可以理解快速排序算法:
//******************************
//描述:快速排序算法
//类名:QuickSort
//作者:洪晓军
//时间:2004-11-2
//******************************
public class QuickSort
{
public void quickSort(int[ ] a,int left, int right) //left为数组a第一个元素位置,right为数组a最后一个元素位置
{
int i, j, pivot, temp; //pivot为支点
if (left >= right)
return;
i = left;
j = right + 1;
pivot = a[left];
while (true)
{
do
{
i++;
} while (a[i] < pivot && i < right); //从左向右寻找大于支点元素
do
{
j--;
} while (a[j] > pivot && j > 0); //从右向左寻找小于支点元素
if (i >= j)
break;
else //满足i<j则交换i和j位置元素值
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
a[left] = a[j];
a[j] = pivot; //以a[j]为新支点,j位置左边元素值均小于a[j],j位置右边元素值均大于a[j]
quickSort(a, left, j - 1); //递归排序
quickSort(a, j + 1, right);
}
}