今天我们来学习一种不浪费空间、效率还非常高的排序算法——快速排序。
算法思想
快速排序的基本思想——分治法。
1. 选取基准数(pivot):选数组中的第一个或者中间一个。
2. 分区:将数组中比基准数小的放的左边,比基准数大的放到右边。
3. 递归:对左右分区重复1、2操作。
过程描述
有这样一个数组arr,长度为11。
13, 34, 23, 8, 12, 17, 12, 42, 4, 9, 19
1) 以第一个数13为基数。我们指定两个变量i(i=0)、j(j=10)分别指向数组两端。我们将第一个数赋给pivot,pivot = arr[i]。这时需要找一个比pivot小的数填充arr[i]。
[Nil], 34, 23, 8, 12, 17, 12, 42, 4, 9, 19 pivot = 13 (i=0,j=10)
2) 首先移动j,从右向左移动,直到找到第一个比pivot小的数。当j=9时,arr[j] < pivot,将arr[j]赋值给arr[i]。此时有需要找一个大于或等于pivot的数,来填充arr[j]。
9, 34, 23, 8, 12, 17, 12, 42, 4, [Nil], 19 pivot = 13 (i=0, j=9)
3) 从左向右移动i,直到找到第一个大于等于pivot的数。当i=1时,arr[i] > pivot,将arr[i]赋值给arr[j]。再找一个比pivot小的数来填充arr[i]。
9, [Nil], 23, 8, 12, 17, 12, 42, 4, 34, 19 pivot = 13 (i=1, j=9)
4) 按照这个规律一直推下去直到i==j。就将pivot赋给arr[i](或者arr[j])。
9,4,23,8,12,17,12,42,[Nil],34,19 pivot = 13 (i=1, j=8)
9,4,[Nil],8,12,17,12,42,23,34,19 pivot = 13 (i=2, j=8)
9,4,12,8,12,17,[Nil],42,23,34,19 pivot = 13 (i=2, j=6)
9,4,12,8,12,[Nil],17,42,23,34,19 pivot = 13 (i=5, j=6)
第一次循环的结果是9, 4, 12, 8, 12, 13, 17, 42, 23, 34, 19。pivot(13)将数组分成了左右两个分区。
5) 利用递归的方式,对左右两个分区做上述操作。
代码实现
先看Java代码实现,结合上面的过程描述以及代码里面的注释,相信不难看明白。
1
2
3
4
|
public static void quickSort(int arr[], int start, int end) {
if
(start < end) { int pivot = arr[start]; int i = start, j = end;
while
(i < j) {
// 从右向左移动j,直到找到第一个小于pivot的数 while(i < j && arr[j] >= pivot)
j--;
if
(i < j) arr[i++] = arr[j];
// 从左向右移动i,直到找到第一个大于等于pivot的数 while(i < j && arr[i] < pivot) i++; if(i < j) arr[j--] = arr[i]; } // 直到i == j,遍历结束,将pivot赋给arr[i] arr[i] = pivot; // 递归调用 quickSort(arr, start, i - 1); quickSort(arr, i + 1, end); } }
|
再看用Scala如何实现
1
2
3
4
|
def sortScala(xs: Array[Int]): Array[Int] = {
if
(xs.length <= 1) xs
else
{ val pivot = xs(xs.length / 2) Array.concat( sortScala(xs filter (pivot >)),
xs filter (pivot ==),
sortScala(xs filter (pivot <))) } }
|
哇哦,简洁程度简直亮瞎双眼。只不过与我们前面描述过程稍微有些差别。这里是将分区分成大于、小于、等于三部分,每次都对递归结果做个合并就OK了,缺点就是它需要更多的转换空间。
如果你想了解scala,可以参照我的另一篇博客Scala极速入门
总结
快速排序是基于分治法、不稳定的排序算法。时间复杂度平均情况为O(n*logn),最差情况为O(n^2)。