引
对于快速排序我们这里有三种优化的方式, 这三种优化方式所针对的是分割(即尽可能的均匀分割)
- 随机选取基准值
- 三数取中法
- 把和基准相同的数据, 从两边移到跟前来
三数取中法是我们最常用的一个方法,也是比较简单好用的方法,因此这里主要介绍三数取中法~
随机选取基准法
基本思路:
以左边做 key , 在 [left + 1 , right] 这个区间来随机获取一个下标, 和 left 下标交换. 所以本质上还是以 left 下标的值作为 key 的, 这种方式在大多情况下, 是能够均匀分割的, 但还是有一定的可能性是会出现单分支的情况的, 因此这种方法不予考虑
三数取中法
对于快排的优化我们可以结合直接插入排序和三数取中法来优化~ 在一定区间内使用直接插入排序,优化区间内的比较. 使用三数取中法,使所找的基准能够均匀分割, 从而降低了递归的深度, 防止栈溢出~
基本思路:
在 [left + 1 , right] 这个区间来比较 left 下标, right 下标和 mid 下标的值, 取值为中间大的数来作为基准值
代码
public void quickSort(int[] arr) {
if (arr == null || arr.length > 2) {
return;
}
qSort(arr, 0, arr.length - 1);
}
private void qSort(int[] arr, int left, int right) {
// left == right 说明就只剩下一个元素了,不需要再找基准了, 但这里不能取 ==
// 因为 left 可能会走到 right 的右边, 因此要取 >=
if (left >= right) {
return;
}
// 在一定区间内使用直接插入排序,优化区间内的比较
// 这里的区间可根据情况自己设置
if (right - left + 1 < 1000) {
// 当这组数据只有 1000 个数据时就使用直接插入排序
insertSortRang(arr, left, right);
}
// 使用三数取中法,使所找的基准能够均匀分割
int index = medianOfThree(arr, left, right);
// 使用挖坑法找基准
int pivot = partition(arr, left, right);
qSort(arr, left, pivot - 1);
qSort(arr, pivot + 1, right);
}
private void insertSortRang(int[] arr, int left, int right) {
for (int i = left + 1; i <= right; i++) {
int temp = arr[i];
int j = left - 1;
for (; j >= 0; j--) {
if (arr[j] > temp) {
arr[j + 1] = arr[j];
} else {
break;
}
}
arr[j + 1] = temp;
}
}
private int medianOfThree(int[] arr, int left, int right) {
int mid = (right - left) >>> 1;
if (arr[left] < arr[right]) {
if (arr[mid] < arr[left]) {
return left;
} else if (arr[mid] > arr[right]) {
return right;
} else {
return mid;
}
} else {
if (arr[mid] > arr[left]) {
return left;
} else if (arr[mid] < arr[right]) {
return right;
} else {
return mid;
}
}
}
private int partition(int[] arr, int left, int right) {
int temp = arr[left];
while (left < right) {
// 这里的 arr[right] >= temp 一定要写 >= 不能写 >
// 否则两个相等的数就会一直交换, 进入死循环~
while (left < right && arr[right] >= temp) {
right--;
}
arr[left] = arr[right];
// 同理,这里也是一样的
while (left < right && arr[left] <= temp) {
left++;
}
arr[right] = arr[left];
}
arr[left] = temp;
return left;
}