在这里介绍另一个重要的排序算法—快速排序。在介绍快排前,我们需要先了解一个算法思想—分治法。
分治法
分治法的字面意思是“分而治之”。把一个复杂的问题,分解成一个个容易解决的小问题来处理。这是很多算法设计的基础。
如果一个问题,可以分割成k个子问题,1 < k <= n,如果这些子问题可解,并可以利用这些子问题的解来求得原问题的解,那么这个问题就可以用分治法来解决。分治和递归往往一同出现,因为反复运用同样的方式来解决问题,和递归的理念吻合。
分治法的使用场景:
- 问题缩小到一定程度容易解决。
- 该问题可以划分成规模较小的数个子问题。
- 子问题的解归并后可以得到原问题的解。
- 每个子问题是独立的,没有重合的公共问题。
接下来我们看快速排序是如何运用分治法来解决问题的。
快速排序
快速排序的基本思路是,从数组中挑选一个数字作为基准数,把比他小的数字放到他的左边,比他大的数字放到右边,遍历完成后,这个基准数的位置index就确定了;然后再以index为界,对左右两边的数组继续进行排序,直到整个数组排序完成。
假设我们现在有以下数组 arr:

- 我们以第一个数字为基准数,我们把这个数字先存到临时变量 x = 30 中。然后我们从头尾两边各设置一枚指针,i = 0, j = arr.length - 1。
- 先从右往左比较对应的值是否小于基准数,如果比30小,那么移动到左边。然后从左往右比较值是否大于基准数,如果是,那么移动到右边。
- 我们移动j,然后我们发现17 < 30 ,因此 arr[i] = 17;
- 然后我们移动i ,我们可以看到60 > 30, 因此 arr[j] = 60
- 这时我们的数组是这样的:

- 反复执行第二步,直到 i 和 j 重合,那么这个位置就是 x 的位置。
最后我们得到的数组如下: 
30 被放在了i = 5的位置,我们发现他左边的数字都比他小,右边的数字都比他大。那么30的位置就是他的最终位置了。然后我们以i = 5 为界,对左右两边继续执行该算法。直到整个数组被排序完成,代码如下:
https://codepen.io/cauzinc/pen/ZEBxwar
/*** @param {原数组} arr* @param {左边界} l* @param {右边界} r* 用数组第一个数字作为基准数,然后左右双指针* 1. 先从右边开始移动指针,寻找比基准小的值,移动到左边* 2. 左边开始移动指针,寻找比基准大的值,移动到右边* 3. 直到左右指针重合,重合的位置就是基准数的位置* 4. 以这个重合位置index为界,然后左右两边递归执行该算法,直到数组排序完成*/function quickSort (arr, l, r) {if (l < r) {let mid = arr[l]let i = llet j = rwhile (i < j) {while (i < j && arr[j] >= mid) {j--}arr[i] = arr[j]while (i < j && arr[i] < mid) {i++}arr[j] = arr[i]}arr[i] = midquickSort(arr, l, i - 1)quickSort(arr, i + 1, r)}}
在快速排序中,我们先确定第一个数字的位置,然后剩下的两边,我们用同样的方式去确定剩下数字的位置。每次排序要解决的问题规模(数组大小)都在逐渐变小,且解决问题的方式是同样的。这样排序问题的复杂度就得到了优化。
