思路
- 找到一个基础值,一般取第一个。
- 从右向左找,找到第一个比基准值小的数停下来;
- 从左向右找,找到第一个比基准值大的数停下来;
- 交换这两个数;
- 继续2-4步进行下一轮的查找替换,直至在基准数的左边都比基准数小,右边都比基准数大。
- 以基准数为界,分成左右数组,分别进行1-5操作。
经典快排实现
private static void quickSort(int[] arr, int start, int end) {
if(start > end) return;
int base = arr[start];
int p1 = start, p2 = end;
while(p1 != p2){
while(arr[p2] >= base && p2 > p1){
p2--;
}
while(arr[p1] <= base && p1 < p2){
p1++;
}
swap(arr, p1, p2);
}
arr[start] = arr[p1];
arr[p1] = base;
quickSort(arr, start, p1 - 1);
quickSort(arr, p1 + 1, end);
}
private static void swap(int[] arr, int j, int i) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void main(String[] args) {
int[] arr = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
quickSort(arr, 0, arr.length - 1);
for (int num : arr) {
System.out.print(num + " ");
}
}
- 快排平均时间复杂度为:O(nlogn)
- 最坏时间复杂度为:O(n²) 也就是冒泡排序
- 空间复杂度:O(logn)
- 不稳定
荷兰国旗问题–快排常量级的优化
荷兰国旗问题:荷兰国旗由红白蓝3种颜色的条纹拼接而成。假设这样的条纹有多条,且各种颜色的数量不一,并且随机组成了一个新的图形。需求是:把这些条纹按照颜色排好,红色的在上半部分,白色的在中间部分,蓝色的在下半部分,我们把这类问题称作荷兰国旗问题。
经典快排每次排序得到一个base基准值,分为两部分。一部分是小,一部分是大。如果数组中有多个相同的base值,依然要进行再次排序,这样对重复值进行多次操作。
考虑优化如下:
// 改进后的快排
// 前面的经典快排是以待排数组第一位作为哨兵,进行比较
// 现在是以待排数组最后一位作为哨兵
public static void quickSortPro(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
quickSortPro(arr, 0, arr.length - 1);
}
static void quickSortPro(int[] arr, int l, int r) {
if (l < r) {
// 这一步是随机快排,可以先跳过不看
// swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
int[] p = partition(arr, l, r);
quickSortPro(arr, l, p[0] - 1);
quickSortPro(arr, p[1] + 1, r);
}
}
/**
* 分为三部分,左边是小于,中间是等于的部分,右边是大于的部分
* @param arr 待排序数组
* @param l 待排起始位置
* @param r 待排结束位置
* @return 返回一个数组,记录相等部分的最左和最右边界两个值。
*/
static int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r;
while (l < more) {
//
if (arr[l] < arr[r]) {
swap(arr, ++less, l++);
} else if (arr[l] > arr[r]) {
swap(arr, --more, l);
// 相同值,只要移动
} else {
l++;
}
}
swap(arr, more, r);
return new int[] { less + 1, more };
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
随机快排
最坏时间复杂度为:O(n²) 退化成为了冒泡排序。
前面两种快排,都是选定固定位置作为哨兵,这样快排的初始顺序影响了快排的时间复杂度。
随机快排将随机选取数组中的一个值作为哨兵,只需要将数组划分前,选取一个随机值替换数组的第一个或者最后一个元素
static void quickSortPro(int[] arr, int l, int r) {
if (l < r) {
// 分组之前的选取随机值
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
int[] p = partition(arr, l, r);
quickSortPro(arr, l, p[0] - 1);
quickSortPro(arr, p[1] + 1, r);
}
}