快速排序算法
核心思路(分治思想)
快速排序采用**分治(Divide and Conquer)**的思想,基本步骤如下:
- 选取基准(Pivot):从数组中选择一个“基准值”。
- 分区(Partition):
- 将数组分成两部分:小于基准值的放左边,大于基准值的放右边(可原地交换实现)。
- 递归排序(Recursion):
- 对左右两个子数组递归调用快速排序,直到区间长度为 1 或 0(递归终止条件)。
分区方法
分区方法 | 核心思路 | 适用场景 |
单指针法(Lomuto) | 选择 pivot,遍历时将小于等于 pivot 的交换到前面 | 适用于普通数据,代码简单 |
双指针法(Hoare) | 选择 pivot,左右指针向中间移动,交换不符合条件的元素 | 适用于大规模数据,交换次数更少 |
三指针法 | 维护 < pivot ,= pivot ,> pivot 三个区间 |
适用于数据中大量相同元素,避免不均匀分区 |
代码实现
双指针法:
- 选择
pivot
(基准值):通常选择 数组的第一个元素arr[low]
作为基准值。 - 双指针遍历,确定分区位置的索引号:
- 左指针
i
:向右移动,寻找到一个大于等于pivot
的元素。 - 右指针
j
:向左移动,寻找到一个小于等于pivot
的元素。 - 交换
arr[i]
和arr[j]
,让小的去左,大的去右。
- 左指针
- 终止条件:如果
i
和j
相遇(即i >= j
),那么说明pivot
已经被正确分区。
function quickSort(arr, start = 0, end = arr.length - 1) { if (start < end) { //分区索引位置 let partitionIndex = partition(arr, start, end); //分区-左侧排序 quickSort(arr, start, partitionIndex); //分区-右侧排序 quickSort(arr, partitionIndex + 1, end); } return arr; } //排序并返回分区索引点 function partition(arr, start, end) { //基准值 let pivot = arr[start]; //由于do while语句的原因,开始节点要-1,结束节点要+1,不然会漏洞头尾元素 let i = start - 1, j = end + 1; while (true) { //小于pivot时就一直移动 do { i++; } while (arr[i] < pivot); //大于pivot时就一直移动 do { j--; } while (arr[j] > pivot); // 完成一次分区 if (i >= j) return j; //es6的解构语法实现交换,避免了临时变量 [arr[i], arr[j]] = [arr[j], arr[i]]; } }
do{ 代码块 } while(条件)
do... while 语句其实是 while 语句的一个变体。该循环会先执行一次代码块,然后对条件表达式进行判断,如果条件为真,就会重复执行循环体,否则退出循环。