引言
「快速排序 quick sort」是一种基于分治策略的排序算法,运行高效,应用广泛。
原理
快速排序的核心操作是“哨兵划分”,其目标是:选择数组中的某个元素作为“基准数”,将所有小于基准数的元素移到其左侧,而大于基准数的元素移到其右侧。步骤如下:
- 选取数组最左端元素作为基准数,初始化两个指针 i 和 j 分别指向数组的两端。
- 设置一个循环,在每轮中使用 i(j)分别寻找第一个比基准数大(小)的元素,然后交换这两个元素。
- 循环执行步骤 2. ,直到 i 和 j 相遇时停止,最后将基准数交换至两个子数组的分界线。
哨兵划分完成后,原数组被划分成三部分:左子数组、基准数、右子数组,且满足“左子数组任意元素 ≤ 基准数 ≤ 右子数组任意元素”。因此,我们接下来只需对这两个子数组进行排序。
算法流程
快速排序的整体流程如图所示。
- 首先,对原数组执行一次“哨兵划分”,得到未排序的左子数组和右子数组。
- 然后,对左子数组和右子数组分别递归执行“哨兵划分”。
- 持续递归,直至子数组长度为 1 时终止,从而完成整个数组的排序。
代码展示
public static void main(String[] args) {
int[] a = {2,4,1,0,3,5};
quickSort(a,0,a.length-1);
for (int i : a) {
System.out.print(i+" ");
}
}
//简单的交换,不用多说吧
public static void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
//哨兵划分
public static int partition(int[] nums, int left, int right) {
//以nums[left]为基准数
int i = left, j = right;
while (i < j) {
while (i < j && nums[j] >= nums[left]) j--;//从右向左找首个小于基准数的元素
while (i < j && nums[i] <= nums[left]) i++;//从左向右找首个大于基准数的元素
swap(nums, i, j);//交换这两个元素
}
swap(nums, left, i);//将基准数交换至两子数组的分界处
return i;//返回基准数的索引
}
public static void quickSort(int[] nums, int left, int right) {
if (left >= right)
return;
//哨兵划分
int pivot = partition(nums, left, right);
//递归左,右子数组
quickSort(nums, left, pivot - 1);
quickSort(nums, pivot + 1, right);
}
尾言
虽然快速排序的平均时间复杂度为O(nlogn),在实际应用中表现优秀,但在某些情况下可能会出现最坏情况,导致时间复杂度退化为O(n^2)。为了提高快速排序的性能,我们可以采用优化方法:三数取中法:在选择基准元素时,不要总是选择第一个或最后一个元素,而是随机选择三个元素,取其中位数作为基准元素。这样可以降低最坏情况的发生概率。