排序方法
- 挑一个元素(pivot)【基准值】
- 小于pivot的值放前面,大于pivot的值放后面
- 将左右两个区域视为两个数组,重复前两个步骤,直到排序完成
排序分析
时间复杂度:
- 最好:O(nlogn),基准值每次为中间值
- 最坏:O(n^2),基准值每次为极值
空间复杂度:
- O(logn)
不稳定排序
原地排序
代码
- 从 left 开始,遇到比基数大的数,记录其下标,与right记录的空位交换;再从 right 往前遍历,找到第一个比基数小的数,记录其下标,与left记录的空位交换。
public static void quickSort(int[] nums, int left, int right) {
if (left >= right) return;
int x = left;
int y = right;
int z = nums[left];
while (x < y) {
while (x < y && nums[y] >= z) --y;
if (x < y) nums[x++] = nums[y];
while (x < y && nums[x] <= z) ++x;
if (x < y) nums[y--] = nums[x];
}
nums[x] = z;
quickSort(nums, left, x - 1);
quickSort(nums, x + 1, right);
}
- 从 left 开始,遇到比基数大的数,记录其下标;再从 right 往前遍历,找到第一个比基数小的数,记录其下标;然后交换这两个数。
// 将 arr 从 start 到 end 分区,左边区域比基数小,右边区域比基数大,然后返回中间值的下标
public static void quickSort(int[] arr, int start, int end) {
// 如果区域内的数字少于 2 个,退出递归
if (start >= end) return;
// 将数组分区,并获得中间值的下标
int middle = partition(arr, start, end);
// 对左边区域快速排序
quickSort(arr, start, middle - 1);
// 对右边区域快速排序
quickSort(arr, middle + 1, end);
}
// 将 arr 从 start 到 end 分区,左边区域比基数小,右边区域比基数大,然后返回中间值的下标
public static int partition(int[] arr, int start, int end) {
// 取第一个数为基数
int pivot = arr[start];
// 从第二个数开始分区
int left = start + 1;
// 右边界
int right = end;
while (left < right) {
// 找到第一个大于基数的位置
while (left < right && arr[left] <= pivot) left++;
// 找到第一个小于基数的位置
while (left < right && arr[right] >= pivot) right--;
// 交换这两个数,使得左边分区都小于或等于基数,右边分区大于或等于基数
if (left < right) {
exchange(arr, left, right);
left++;
right--;
}
}
// 如果 left 和 right 相等,单独比较 arr[right] 和 pivot
if (left == right && arr[right] > pivot) right--;
// 将基数和轴交换
exchange(arr, start, right);
return right;
}
private static void exchange(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}