快速排序
介绍
快速排序的最坏运行情况是 O ( n ² ) O(n²) O(n²),比如说顺序数列的快排。它的平均期望时间是 O ( n l o g n ) O(nlogn) O(nlogn),对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。
步骤
- 从数列中挑出一个元素,称为 “基准”(pivot);
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。
- 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
演示
推荐排序算法动态调试、演示网站: https://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html
图片来源于:https://zhuanlan.zhihu.com/p/93129029,为一次快排的过程
代码与思想
/**
* TODO 快速排序
* 思想:递归,双指针
*
* @author nanfeng
* @date 2022/03/28/ 14:29
*/
public class QuickSort {
public static void main(String[] args) {
int[] nums = new int[]{711,42,333,6,7555,85,93,24,30,177};
// 有两个相等的值的情况
// int[] nums = new int[]{711,42,333,6,7555,85,85,24,30,177};
// 左、右指针为左闭右闭
quickSort(nums, 0, nums.length-1);
System.out.println(Arrays.toString(nums));
}
static void quickSort(int[] nums,int left, int right) {
// 递归的结束条件 左指针大于右指针:
if(left > right) return;
// 保留左右指针位置
int start = left, end = right;
// 选择一个基准(比较值),快排每一次递归可以确定一个基准的位置,默认采取左指针的值,循环内先遍历右侧,反之同理
int pivot = nums[left];
// 循环结束条件:左右指针相等时,说明确定了基准的位置
while (start != end) {
// 循环结束条件依然保证左右指针相同(因为循环内 右指针变化),同时保证基准的右侧全是大于其的值
// 这里有一个注意点
// 当待排序数组有重复值时,应在左侧或右侧任意一侧处理相等值
while (end > start && nums[end] >= pivot) end--;
// 此时右指针指向的值小于基准,将其赋值到左指针处(因为此时左指针的值是大于基准的数)
nums[start] = nums[end];
// 同理
while (start < end && nums[start] < pivot) start++;
// 此时左指针指向的值大于基准,将其赋值到右指针处(因为此时右指针的值是小于基准的数)
nums[end] = nums[start];
}
// 经过一次递归(一次快排) 确定一个基准值的最终位置,此时左右指针指向同一个位置
nums[start] = pivot;
// 小于基准值元素的子数列 进行一次快排
quickSort(nums, left, start-1);
// 大于基准值元素的子数列 进行一次快排
quickSort(nums, start + 1, right);
}
}
参考: