|
18 | 18 |
|
19 | 19 | ### 时间复杂度 |
20 | 20 |
|
| 21 | +``` |
| 22 | + public void sort(int[] nums, int start, int end) { |
| 23 | + if (start >= end) { |
| 24 | + return; |
| 25 | + } |
| 26 | +
|
| 27 | + int pivotIdx = partition(nums, start, end); // 时间复杂度为O(n) |
| 28 | + sort(nums, start, pivotIdx - 1); // 时间复杂度为T() |
| 29 | + sort(nums, pivotIdx + 1, end); |
| 30 | + } |
| 31 | +``` |
| 32 | + |
| 33 | +在快排算法中,数组会被分成两个子序列,长度分别为pivotIdx和n-pivotIdx-1,对两个子序列调用递归排序的时间复杂度为T(pivotIdx)和T(n-pivotIdx-1);另外,partition部分的时间复杂度为O(n) |
| 34 | +。所以快排的总时间复杂度为三者相加,即T(n) = T(pivotIdx)+T(n-pivotIdx-1)+O(n)。特别地,T(0) = T(1) = 0。 |
| 35 | + |
21 | 36 | **最优时间复杂度**:$O(n\log n)$ |
22 | 37 |
|
23 | | -**最差时间复杂度**:O(n<sup>2</sup>) |
| 38 | +最优的情况是每次数组分割得到的两个子序列长度均等。这种情况下,算法的时间复杂度T(n) = T($\frac{n}{2}$)+T($\frac{n}{2}$)+O(n) |
| 39 | + |
| 40 | +*时间复杂度推导*: |
| 41 | + |
| 42 | +$$\begin{aligned} T(n) &= 2T(\frac{n}{2})+n \\ &= 2(2T(\frac{n}{2})+\frac{n}{2})+n \\ &= 4T(\frac{n}{4})+2n \\ &= 4(2T( |
| 43 | +\frac{n}{8})+\frac{n}{4})+2n \\ &= 8T(\frac{n}{8})+3n \\ &= ... ... \\ &= 2^kT(\frac{n}{2^k})+kn \\ &= nT(1)+n\log n \\ |
| 44 | +&= O(n\log n) \\ \end{aligned}$$ |
| 45 | + |
| 46 | +**最差时间复杂度**:$O(n^2)$ |
| 47 | + |
| 48 | +最差的情况是每次数组分割得到的两个子序列长度为n-1和0,即每次都选择了当前最大或者最小的元素作为基准。这种情况下,算法的时间复杂度T(n) = T(n-1)+T(0)+O(n) = T(n-1)+O(n) |
| 49 | + |
| 50 | +*时间复杂度推导*: |
| 51 | + |
| 52 | +$$\begin{aligned} T(n) &= T(n-1)+n \\ &= T(n-2)+(n-1)+n \\ &= T(n-3)+(n-2)+(n-1)+n \\ &= ... ... \\ &= T(0)+1+2+...+( |
| 53 | +n-1)+n \\ &= T(0)+\frac{n*(n+1)}{2} \\ &= O(n^2) \\ \end{aligned}$$ |
24 | 54 |
|
25 | 55 | **平均时间复杂度**:$O(n\log n)$ |
26 | 56 |
|
27 | 57 | ### 空间复杂度 |
28 | 58 |
|
| 59 | +partition函数是在原有数组上进行操作,空间复杂度为O(1),所以快排的空间复杂度主要来自于递归所造成的的栈空间的使用,所以需要知道递归栈的深度。 |
| 60 | + |
29 | 61 | **最优空间复杂度**:$O(\log n)$ |
30 | 62 |
|
31 | | -**最差空间复杂度**:O(n) |
| 63 | +最优的情况下,每次数组都被分割为两个均等的子序列,设栈的深度为k,则有$n(\frac{1}{2})^k$=1,可得$k=\log n$,即空间复杂度为O($\log n$)。 |
| 64 | + |
| 65 | +**最差空间复杂度**:$O(n)$ |
| 66 | + |
| 67 | +最差的情况下,每次数组都被分割成长度为n-1和0的两个子序列,所以递归的深度为n,每次递归所需要的空间是固定的,所以总的空间复杂度为O(n)。 |
32 | 68 |
|
33 | 69 | **平均空间复杂度**:$O(\log n)$ |
34 | 70 |
|
35 | 71 | ## 稳定性分析 |
36 | 72 |
|
| 73 | +快排是不稳定的排序算法,重复元素在排序前后的相对位置可能会发生改变,e.g. [4, 3, 5, 3],基准选取策略为选取第一个元素。 |
| 74 | + |
37 | 75 | ## 扩展/优化 |
| 76 | + |
| 77 | +快排算法的优化主要体现在pivot的选取上,常见的选取方法有三种:固定选取,随机选取,三数取中。 |
| 78 | + |
| 79 | +**固定选取** |
| 80 | + |
| 81 | +选择待排序数组中固定位置的元素作为基准,一般选择第一个元素或者最后一个元素。 |
| 82 | + |
| 83 | +**随机选取** |
| 84 | + |
| 85 | +随机选取待排序数组中的一个元素作为基准。 |
| 86 | + |
| 87 | +**三数取中** |
| 88 | + |
| 89 | +取待排序数组的第一个元素、中间元素和最后一个元素,取三个数排序后中间的那个元素作为基准。这种方法可以有效避免最差情况的发生(i.e. 待排序数组已有序,包括正序和逆序,基准为最大值或者最小值,导致时间复杂度和空间复杂度最大)。 |
| 90 | + |
| 91 | +## Refs |
| 92 | + |
| 93 | +1. [【算法】快速排序](https://www.cnblogs.com/HDK2016/p/6876313.html#a31) |
0 commit comments