基础排序算法六——快速排序

Posted 聊聊Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础排序算法六——快速排序相关的知识,希望对你有一定的参考价值。

快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

实施快速排序步骤:

1)从列表中选择一个名为pivot的元素。 通常枢轴可以是中间索引元素。
2)对列表进行重新排序,使得值小于pivot的所有元素都出现在pivot之前,而所有值大于pivot的元素都会在pivot之后(相等的值可以以任何方式)。 分区之后,枢轴处于最终位置。 这被称为分区操作。
3)递归地将上述步骤应用于具有较小值的元素的子列表以及分别具有较大值的元素的子列表。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import java.util.Arrays;

/**
* @author amosli
* @date 2018-06-15  18:03
*/
public class QuickSort {


   static int num = 0;

   public static void main(String[] args) {
       int[] array = new int[]{2, 8, 5, 3, 9, 4, 1, 7};
       sort(array);
       System.out.println("最终排序结果:" + Arrays.toString(array));
   }

   private static void sort(int[] array) {

       quickSort(array, 0, array.length - 1);

   }

   private static void quickSort(int[] array, int lower, int higher) {
       int i = lower;
       int j = higher;
       int pivot = array[(lower + higher) / 2];

       while (i <= j) {
           while (array[i] < pivot) {
               i++;
           }

           while (array[j] > pivot) {
               j--;
           }

           if (i <= j) {
               swap(array, i, j);
               i++;
               j--;
           }
       }

       System.out.println("num:" + (++num) + "," + Arrays.toString(array));

       if (lower < j) {
           quickSort(array, lower, j);
       }

       if (i < higher) {
           quickSort(array, i, higher);
       }

   }

   private static void swap(int[] array, int i, int j) {
       int temp = array[i];
       array[i] = array[j];
       array[j] = temp;
   }
}

输出:

1
2
3
4
5
6
7
num:1,[2, 1, 3, 5, 9, 4, 8, 7]
num:2,[1, 2, 3, 5, 9, 4, 8, 7]
num:3,[1, 2, 3, 5, 9, 4, 8, 7]
num:4,[1, 2, 3, 4, 9, 5, 8, 7]
num:5,[1, 2, 3, 4, 5, 9, 8, 7]
num:6,[1, 2, 3, 4, 5, 7, 8, 9]
最终排序结果:[1, 2, 3, 4, 5, 7, 8, 9]


快速排序复杂度分析

快速排序的时间性能取决于快速排序递归的深度,可以用递归树来描述递归算法的执行情况。{50,10,90,30,70,40,80,60,20}在快速排序过程中的递归过程。由于我们的第一个关键字是50,正好是待排序的序列的中间值,因此递归树是平衡的,此时性能也比较好。

在最优情况下,Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为(表示不大于x的最大整数),即仅需递归log2n次,需要时间为T(n)的话,第一次Par-tiation应该是需要对整个数组扫描一遍,做n次比较。然后,获得的枢轴将数组一分为二,那么各自还需要T(n/2)的时间(注意是最好情况,所以平分两半)。于是不断地划分下去,我们就有了下面的不等式推断。

在最优的情况下,快速排序算法的时间复杂度为O(nlogn)。

在最坏的情况下,待排序的序列为正序或者逆序,每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。如果递归树画出来,它就是一棵斜树。此时需要执行n-1次递归调用,且第i次划分需要经过n-i次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为sigma(i=1, n-1, n-i)=(n-1)+(n-2)+…+1=n(n-1)/2,最终其时间复杂度为O(n2)。

平均的情况,设枢轴的关键字应该在第k的位置(1≤k≤n)

由数学归纳法可证明,其数量级为O(nlogn)。

就空间复杂度来说,主要是递归造成的栈空间的使用,最好情况,递归树的深度为log2n,其空间复杂度也就为O(logn),最坏情况,需要进行n-1递归调用,其空间复杂度为O(n),平均情况,空间复杂度也为O(logn)。

由于关键字的比较和交换是跳跃进行的,因此,快速排序是一种不稳定的排序方法。

jdk 中的排序

使用的是优化后的快排,具体请查看:
DualPivotQuicksort

参考

  • http://www.java2novice.com/java-sorting-algorithms/quick-sort/

  • https://zongwenlong.github.io/2017/01/06/Java-SourceCode-Sort/

  • 《大话数据结构》



如有不懂,查看原文的视频解说。

以上是关于基础排序算法六——快速排序的主要内容,如果未能解决你的问题,请参考以下文章

《算法零基础100讲》(第37讲) 排序进阶 - 快速排序

算法排序之堆排序

图解算法基础--快速排序,附 Go 代码实现

数据结构与算法基础 模块六

基础排序算法总结(代码+图片分析)

快速排序-递归实现