快速排序是一种优雅的算法,作者C.A.R.Hoare曾说,当它最初开发出Quicksort时,他认为这种算法太简单了,不值得发表,而且直到能够分析这种算法的预期运算时间之后,他才写出经典的“Quicksort”论文。
在最坏情况下,Quicksort可能需要 n^2 的时间来对数组元素进行排序。而在最优的情况下,它将选择中值作为划分元素,因此只需 nlgn 次的比较就可以完成数组的配需。
快速排序的基本思想是基于分治策略的。要研究这种算法,那么必须说到冒泡法排序,因为快速排序是对冒泡排序的一宗改进。它的基本思想是,通过一趟排序将戴记录分割成独立的两个部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行配需,以达到整个序列有序。
作为一种分治策略包括三个步骤:分解-递归求解-合并;它包括三个核心部分:比较-置换-递归 。
好,我们直接看伪代码:
2 int Partition(SqList & L, int low, int high) {
3 //交换顺序表L.r[low..high]的记录,使枢轴记录到位,并返回其所在位置,此时
4 //在它之前(后)的记录均不大(小)与它的。
5 L.r[0] = L.r[low];
6 pivotkey = L.r[low].key; //用子表的第一个记录做枢轴记录
7 while( low < high ){ //从表的两端交替的向中间扫描
8 while( low < hith && L.r[high].key >= pivotkey )
9 -- high;
10 L.r[low] = L.r[high]; //将此枢轴记录小的记录交换到低端
11 while(low <high && L.r[low].key <= pivotkey)
12 ++ low;
13 L.r[low] = L.r[high]; //将比枢轴记录打的记录交换到高端
14 }
15 L.r[low] = L.r[0];
16 return low; //返回枢轴所在位置
17}
18
19
20 // 递归形式的快速排序
21 void QSort(SqList & L, int low , int high) {
22 //对顺序表L的子序列L.r[lowhigh]做快速排序
23 if( low < high){
24 pivotloc = Partition(L, low, high);
25 QSort(L, low, pivotloc -1 );
26 QSort(L,pivotloc + 1,high);
27 }//
28}
在这里,我给出一个 C# 的实现,而非 C 语言实现 (如果有必要的话我想是应该列出来的,不过这可能大多数人并不敢兴趣,或者并不是主要关注的)。
2 /// 指定数组起始和结束位置,对数组进行快速排序
3 /// </summary>
4 /// <param name="startIndex">数组起始下标</param>
5 /// <param name="endIndex">数组结束下标</param>
6 /// <param name="x">排序数组</param>
7 public static void QuickSort( int startIndex, int endIndex, int [] x)
8 {
9 int i, m;
10 if (startIndex >= endIndex) return;
11 Swap(startIndex, RandomUtil.NextInt(startIndex, endIndex), x);
12 m = startIndex;
13 for (i = startIndex + 1; i <= endIndex; i++)
14 {
15 if (xIdea < x[startIndex])
16 {
17 Swap(++m, i, x);
18 }
19 }
20 Swap(startIndex, m, x);
21 QuickSort(startIndex, m - 1, x);
22 QuickSort(m + 1, endIndex, x);
23 }
24
25 /**/ /// <summary>
26 /// 交换数组两个元素
27 /// </summary>
28 /// <param name="a">数字A</param>
29 /// <param name="b">数字B</param>
30 private static void Swap( int a, int b, params int [] x)
31 {
32 int temp = xAngel;
33 xAngel = xBeer;
34 xBeer = temp;
35 }
36
在C#中如果要对一个数组进行排序,一般就是调用 Sort(T[] array)方法,这个方法首先判断参数array是否为空,如果是则抛出System.ArgumentNullException 异常,否则它将调用 Sort(T[] array, int index, int length, IComparer comparer)方法对 array 进行排序,这个方法最后会调用 System 命名空间中的一个名为 ArraySortHelper 的类中的一个名为QuickSort(T[] keys, TValue[] values, int left, int right, IComparer comparer) 的方法,很明显这个就是排序中的核心算法了,这就是一个快速排序法,微软对这个方法进行了优化以及扩展,这种做法让排序非常的灵活。
下面,我们一步一步的扒开整一个过程。
那么我们就从一个假设开始吧。假如我定义了如下一个数组 object[] objects = {'a',123,1,"Test"}; 然后调用 Array.Sort(objects) 对它进行排序,微软对这个方法的描述是:
//
// 摘要:
// 使用 System.Array 的每个元素的 System.IComparable 泛型接口实现,对整个 System.Array 中的元素进行排序。
//
// 参数:
// array:
// 要排序的从零开始的一维 System.Array。
//
// 异常:
// System.ArgumentNullException:
// array 为 null。
//
// System.InvalidOperationException:
// array 中的一个或多个元素未实现 System.IComparable 泛型接口。
简而言之,就是使用比较两个对象而实现的方法对数组进行排序。那么这个方法是做什么的呢?在描述中我们还可以看到一个 System.ArgumentNullException异常,这个异常就是前面我们所说的这个方法首先判断参数array是否为空,然后抛出这个异常的,最后它将返回 Array. Sort(T[] array, int index, int length, IComparer comparer); 实现排序,这个方法的描述是:
//
// 摘要:
// 使用 System.Array 的每个元素的 System.IComparable 泛型接口实现,对 System.Array 中某个元素范围内的元素进行排序。
//
// 参数:
// array:
// 要排序的从零开始的一维 System.Array。
//
// length:
// 排序范围内的元素数。
//
// index:
// 排序范围的起始索引。
//
// 异常:
// System.ArgumentException:
// index 和 length 不指定 array 中的有效范围。
//
// System.ArgumentNullException:
// array 为 null。
//
// System.ArgumentOutOfRangeException:
// index 小于 array 的下限。- 或 -length 小于零。
//
// System.InvalidOperationException:
// array 中的一个或多个元素未实现 System.IComparable 泛型接口。
这个方法是 Array.Sort 系列重载的终止。(注:包括Sort(T[] array); Sort(T[] array, IComparer comparer); Sort(T[] array, Comparison comparison); Sort(T[] array, int index, int length);)
这个方法实质也做的是对参数做一系列的判断然后抛出异常,最后如果参数都没有问题的话,它就调用 System. ArraySortHelper(这个类包括了两个重要的算法,一个是我们现在讲的快速排序,另外一个是什么呢?下篇文章我将会讲到,值得期待的。)的 Sort(array, index, length, comparer);方法。当然在调用 ArraySortHelper 中使用了不小的技巧对性能是做到至极的,好抛开这些 这个 Sort 方法又是干什么的呢?哈哈,不要以为它会感谢什么了不起的事情,它起始就调用ArraySortHelper中的 Sort(T[] keys, TValue[] values, int index, int length, IComparer comparer);方法而已。它是这样调用的:
this.Sort(items, null, index, length, comparer);
原来是对object 类型排序了。你可能会认为,这个方法要做的就是对数组排序了吧,那就错了。它要做的是对comparer参数进行一下判断以及设定,然后调用:QuickSort(T[] keys, TValue[] values, int left, int right, IComparer comparer); 这个才是排序的核心算法。
废话少说,马上看它的实现。
2 /// 指定左偏移,右偏移和比较器接口使用快速排序法对T[]进行排序
3 /// </summary>
4 /// <typeparam name="TValue">值类型</typeparam>
5 /// <param name="keys">关键字数组</param>
6 /// <param name="values">值数组</param>
7 /// <param name="left">左偏移</param>
8 /// <param name="right">右偏移</param>
9 /// <param name="comparer">比较器接口</param>
10 private void QuickSort < TValue > (T[] keys, TValue[] values, int left, int right, IComparer < T > comparer)
11 {
12 // keys 和 values 是哈希表时候用
13 do
14 {
15 int a = left;
16 int b = right;
17 // num3起始可以看做是一个随机数,也是特意安排的,它是取 left 到 right 的中间值
18 //因为如果待排序数组本来就是有序的,那么就能只需递归一次
19 int num3 = a + ((b - a) >> 1);
20
21 //交换keys起始位置元素和中间元素, ex: {5,3,6,7,9} -> {6,3,5,7,9}
22 this.SwapIfGreaterWithItems<TValue>(keys, values, comparer, a, num3);
23 //交换keys中间元素和最后元素 {6,3,5,7,9} -> {9,3,5,7,6}
24 this.SwapIfGreaterWithItems<TValue>(keys, values, comparer, a, b);
25 //交换keys中间元素和最后元素 {9,3,5,7,6} ->{9,3,6,7,5}
26 this.SwapIfGreaterWithItems<TValue>(keys, values, comparer, num3, b);
27 // 不要小看上面那三个交换,这是内有玄机的。
28
29 //分界点
30 T y = keys[num3];
31 do
32 {
33 try
34 {
35 while (comparer.Compare(keysAngel, y) < 0)
36 {
37 a++;
38 }
39 while (comparer.Compare(y, keysBeer) < 0)
40 {
41 b--;
42 }
43 }
44 //数组越界异常
45 catch (IndexOutOfRangeException)
46 {
47 //throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", new object[] { y, y.GetType().Name, comparer }));
48 }
49 //其他异常
50 catch (Exception exception)
51 {
52 //throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), exception);
53 }
54 if (a > b)
55 {
56 break;
57 }
58 if (a < b)
59 {
60 T local2 = keysAngel;
61 keysAngel = keysBeer;
62 keysBeer = local2;
63 if (values != null)
64 {
65 TValue local3 = valuesAngel;
66 valuesAngel = valuesBeer;
67 valuesBeer = local3;
68 }
69 }
70 a++;
71 b--;
72 }
73 while (a <= b);
74 if ((b - left) <= (right - a))
75 {
76 if (left < b)
77 {
78 this.QuickSort<TValue>(keys, values, left, b, comparer);
79 }
80 left = a;
81 }
82 else
83 {
84 if (a < right)
85 {
86 this.QuickSort<TValue>(keys, values, a, right, comparer);
87 }
88 right = b;
89 }
90 }
91 while (left < right);
92 }
93
94
95 /**/ /// <summary>
96 /// 交换keys和values数组中的两个元素
97 /// </summary>
98 /// <typeparam name="TValue">值数组</typeparam>
99 /// <param name="keys">关键字数组</param>
100 /// <param name="values">排序数组</param>
101 /// <param name="comparer">比较器接口</param>
102 /// <param name="a">数组下标A</param>
103 /// <param name="b">数组下标B</param>
104 private void SwapIfGreaterWithItems < TValue > (T[] keys, TValue[] values, IComparer < T > comparer, int a, int b)
105 {
106 if (a != b)
107 {
108 try
109 {
110 //如果 keysAngel大于keysBeer则调换
111 if (comparer.Compare(keysAngel, keysBeer) > 0)
112 {
113 T local = keysAngel;
114 keysAngel = keysBeer;
115 keysBeer = local;
116 if (values != null)
117 {
118 TValue local2 = valuesAngel;
119 valuesAngel = valuesBeer;
120 valuesBeer = local2;
121 }
122 }
123 }
124 //数组越界异常
125 catch (IndexOutOfRangeException)
126 {
127 //throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", new object[] { keysBeer, keysBeer.GetType().Name, comparer }));
128 }
129 //其他异常
130 catch (Exception exception)
131 {
132 //throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), exception);
133 }
134 }
135 }
136
其核心算法和基本的快速排序是有所改变的。
欢迎大家指点批评!