思路
- 分区:从待排区域任选一个元素 pivot,大于它放到前面,小于它放到后面。最后会发现 pivot 所在就是有序序列中的位置。
- 递归:以 pivot 为分界,对左右两边进行同样的分区操作
- 递推公式:quick_sort(p...q)=quick_sort(quick_sort(p...r) ; quick_sort(i+1...q))
- 终止条件:p>=q,该分区只剩下一个元素
- 时间复杂度:
- 最好:O(nlogn),在每次 pivot 都可以将大区间一分为二
- 最坏: O(n^2),已经有序,每次选择最后一个元素进行分区
算法实现
重点在于分区函数,分区的实现有多种方式
public class Solution {
public static void quickSort(int[] array) {
quickSort(array, 0, array.length - 1);
}
// 递归
public static void quickSort(int[] array, int start, int end) {
// 终止条件
if (start >= end) {
return;
}
// 分区
int pivot = partition(array, start, end);
// 递归
quickSort(array, start, pivot - 1);
quickSort(array, pivot + 1, end);
}
public static void main(String[] args) {
int[] array = new int[]{3, 5, 1, 9, 6, 2};
quickSort(array);
for (int i : array) {
System.out.print(i + " ");
}
}
}
交换位置函数:
// 交换位置
public static void swap(int[] array, int start, int end) {
int temp = array[start];
array[start] = array[end];
array[end] = temp;
}
分区方式1
- 类似于选择排序,
- 分为已处理和未处理两个区间,已处理区间内元素均小于 pivot
- 选择排序是从未处理区间找到最小值增加到已处理区间末尾
- 快速排序是从未处理区间找到比 pivot 小的值,通过交换 swop 放到已处理区间的末尾
- 将未处理区间的末尾作为 pivot
- 检查完毕将 pivot 放到已处理区间末尾,此时已处理区间元素均小于 pivot
- 注意 i 处于已处理区间末尾+1,把小于 pivot 的放到这里即可
- 使用 j 遍历所有除元素
// 分区
public static int partition(int[] array, int start, int end) {
for (int i = start; i < end - 1; i++) {
if (array[i] <= array[end]) {
swap(array, start, i);
start++;
}
}
swap(array, start, end);
return start;
}
分区方式2
- 分为已处理+未处理+已处理 3 个区间
- 把 start 作为 pivot
- 先从后往前与 pivot 比较,
- 大于等于则将 end 指针前移,end 后面的都是比 pivot 大的
- 小于,则与 pivot 交换位置,并且 start 向前移动一位,start 前面的都是比 pivot 小的
- 交换以后则从前往后与 pivot 比较
- 直到 start=end,则确定 pivot 的位置
- 优化:利用哨兵,不用交换
// 分区
public static int partition(int[] array, int start, int end) {
int temp = array[start];
while (start < end) {
while (start < end && array[end] >= temp) {
end--;
}
swap(array, start, end);
start++;
while (start < end && array[start] <= temp) {
start++;
}
swap(array, start, end);
end--;
}
return start;
}
// 利用哨兵
// 分区
public static int partition(int[] array, int start, int end) {
int temp = array[start]; // 哨兵
while (start < end) {
while (start < end && array[end] >= temp) {
end--;
}
array[start] = array[end];
start++;
while (start < end && array[start] <= temp) {
start++;
}
array[end] = array[start];
end--;
}
array[start] = temp;
return start;
}