快速排序
描述
快速排序借用了分治的思想, 并且基于冒泡排序做了改进。 它将数组拆分为两个子数组, 其中一个子数组的所有元素都比另一个子数组的元素小, 然后对这两个子数组再重复进行上述操作, 直到数组不可拆分, 排序完成。
基本思想
- 从数组中取出一个数,称之为基数(pivot)
- 遍历数组,将比基数大的数字放到它的右边,比基数小的数字放到它的左边
- 遍历完成后,数组被分成了左右两个区域
- 将左右两个区域视为两个数组,重复前两个步骤,直到排序完成
实现
基本框架
sortArray:入口方法QuickSort:递归方法,负责不停的划分,直到pq指针对撞partition: 划分函数,根据pivot划分区域,然后返回中点,中点右边的值均大于pivot,左边的值均小于pivot
1 | // 排序函数 |
第一种写法
按照基本思想进行
- 选取一个基准点
pivot, 定义两个指针i = p,j = p + 1 - 移动
j指针找到 比pivot小的,移动i指针,将其与i换位 - 直到
j > q之后跳出循环 - 最后将
p与i进行互换,返回i指针
1 | function partition(arr, p, q){ |
详细过程
arr = [15, 9, 31, 21, 44, 22, 33, 18, 2]p = 0q = arr.length - 1 = 8pivot = arr[0]i = 0j = 1开始比对9 < 15i++; i = 1swap(1, 1)arr不变31 > 15,21 > 15,44 > 15,22 > 15,33 > 15,18 > 15(跳过)2 < 15i++; i = 2swap(2, 8)[15, 9, 2, 21, 44, 22, 33, 18, 31]
i = 2, p = 0swap(0, 2)[2, 9, 15, 21, 44, 22, 33, 18, 31]返回
i = 2,QuickSort(arr, 0, 1)QuickSort(arr, 3, 8)QuickSort(arr, 0, 1)arr = [2, 9, 15, 21, 44, 22, 33, 18, 31]p = 0q = 1i = 0j = 2swap(0, 0)返回i = 0,QuickSort(arr, 0, -1)(跳过)QuickSort(arr, 1, 1)(跳过)QuickSort(arr, 3, 8)=>partition(arr, 3, 8)[..., 21, 44, 22, 33, 18, 31]i = 3j = 4pivot = 2144 > 21,22 > 21,33 > 21(跳过)18 < 21i = 4, j = 7swap(4, 7)[..., 21, 18, 22, 33, 44, 31]31 > 21(跳过),i = 4, p = 3,swap(3, 4)[..., 18, 21, 22, 33, 44, 31]
返回
i = 4QuickSort(arr, 3, 3)(跳过)QuickSort(arr, 5, 8)=>partition(arr, 5, 8)[..., 22, 33, 44, 31]i = 5, j = 6pivot = 22(跳过)返回
i = 5QuickSort(arr, 4, 4)QuickSort(arr, 6, 8)[..., 33, 44, 31]i = 6, j = 7, pivot = 3344 > 33(跳过)31 < 33i = 7, j = 8swap(7, 8)[..., 33, 31, 44]i = 7, p = 6swap(6, 7)[..., 31, 33, 44]
返回
i = 7QuickSort(arr, 6, 6)(跳过)QuickSort(arr, 8, 8)(跳过)排序完成
[2, 9, 15, 18, 21, 22, 31, 33, 44]
第二种写法
1 | var partition = (arr, p, q) => { |
详细过程
[31, 15, 18, 22, 33, 21, 44, 2, 9]pivot = 31, i = 1, j = 815, 18, 22 < 31(跳过)33 > 31i = 4 j = 8swap(4, 8)[31, 15, 18, 22, 9, 21, 44, 2, 33]j = 79, 21 < 31(跳过)44 > 31i = 6 j = 7swap(6, 7)[31, 15, 18, 22, 9, 21, 2, 44, 33]j = 6- 跳出循环
arr[6] = 2 < 31swap(0, 6)[2, 15, 18, 22, 9, 21, 31, 44, 33]返回j = 6
QuickSort(arr, 0, 5)和QuickSort(arr, 7, 8)QuickSort(arr, 0, 5)=>partition(arr, 0, 5)[2, 15, 18, 22, 9, 21]pivot = 2, i = 1, j = 515 > 2swap(1, 5)=>[2, 21, 18, 22, 9, 15]j = 421 > 2swap(1, 4)=>[2, 9, 18, 22, 21, 15]j = 39 > 2swap(1, 3)=>[2, 22, 18, 9, 21, 15]j = 222 > 2swap(1, 2)=>[2, 18, 22, 9, 21, 15]j = 1- 跳出循环
arr[1] = 18 > 2j--swap(0, 0)返回j = 0
QuickSort(arr, 0, -1)(跳过)和QuickSort(arr, 1, 5)QuickSort(arr, 1, 5)=>partition(arr, 1, 5)[18, 22, 9, 21, 15]pivot = 18, i = 2, j = 522 > 18swap(2, 5)=>[18, 15, 9, 21, 22]j = 415, 9 < 18(跳过)21 > 18此时i = j = 4- 跳出循环
arr[4] = 21 > 18j--swap(2, 3)返回j = 3[9, 15, 18, 21, 22]
QuickSort(arr, 1, 2)(跳过)和QuickSort(arr, 4, 5)(跳过)- 回到
QuickSort(arr, 7, 8)交换后完成排序[2, 9, 15, 18, 21, 22, 31, 33, 44]
第三种写法
1 | var partition = (arr, p, q) => { |
详细过程
[22, 2, 18, 31, 33, 9, 15, 44, 21]pivot = 22i = 1, j = 82, 18 < 22(跳过),31 > 22i = 321 < 22j = 8swap(3, 8)[22, 2, 18, 21, 33, 9, 15, 44, 31]21 < 22(跳过)33 > 22i = 431, 44 > 22(跳过)15 < 22j = 6swap(4, 6)[22, 2, 18, 21, 15, 9, 33, 44, 31]15, 9 < 22跳过,i = 6j = 6跳出循环arr[6] = 33 > 22j--swap(0, 5)[9, 2, 18, 21, 15, 22, 33, 44, 31]返回j = 5
QuickSort(arr, 0, 4)和QuickSort(arr, 6, 8)QuickSort(arr, 0, 4)=>partition(arr, 0, 4)[9, 2, 18, 21, 15]pivot = 9, i = 1, j = 42 < 9(跳过)18 > 9i = 215, 21, 18 > 9(跳过)j = 2跳出循环arr[j] = 18 > 9j--swap(0, 1)[2, 9, 18, 21, 15]返回j = 1
QuickSort(arr, 0, 0)(跳过) 和QuickSort(arr, 2, 4)QuickSort(arr, 2, 4)=>partition(arr, 2, 4)[18, 21, 15]pivot = 18, i = 3, j = 421 > 18i = 315 < 18j = 4swap(3, 4)[18, 15, 21]j = 4跳出循环arr[4] = 21 > 18j--swap(2, 3)[15, 18, 21]返回j = 3
QuickSort(arr, 2, 2)(跳过) 和QuickSort(arr, 4, 4)(跳过)- 回到
QuickSort(arr, 6, 8)=>partition(arr, 6, 8)[33, 44, 31]pivot = 33, i = 7, j = 844 > 33i = 731 < 33j = 8swap(7, 8)[33, 31, 44]j = 8跳出循环arr[8] > 33j--swap(6, 7)[31, 33, 44]返回j = 7
QuickSort(arr, 6, 6)(跳过) 和QuickSort(arr, 8, 8)(跳过)- 返回数组
[2, 9, 15, 18, 21, 22, 31, 33, 44]完成排序
三种方式对比
优化角度
分析上面三个版本的实现,我们可以发现,在随机化越高的情况下,快速排序所用的轮次会越少,所以一般我们可以通过打乱数组后进行排序,效率更高
1 | var swap = (arr, i, j) => { |
完整代码
1 | var sortArray = function(arr){ |
再补一个三路快排
1 | // 3路快排 [l,...lt, .. i, ... gt, r] |
算法复杂度
| 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 排序方式 | 稳定性 |
|---|---|---|---|---|---|
| O(nlogn) | O(nlogn) | O(n^2) | O(n) | in-place | 不稳定 |