快速排序优化

快排是效率很高的排序方法,但仍有一些问题,如稳定性不高,处理小数组效率不算很高,处理重复数字效率低,难以选取最优中轴

主要有三种优化方式

  • 使用插入排序处理小数组
  • 三值交换选取中轴
  • 聚集重复元素

中轴处理

/**
* 前 中 后三者排序 并把中轴放在high - 1的位置 如{3,4,2,6,1} 会处理为 {1,4,6,2,3}
*/
public static void dealPivot(int[] arr, int left, int right) {
int mid = (left + right) / 2;
if (arr[left] > arr[mid]) {
swap(arr, left, mid);
}
if (arr[left] > arr[right]) {
swap(arr, left, right);
}
if (arr[right] < arr[mid]) {
swap(arr, right, mid);
}
swap(arr, right - 1, mid);
}

快排主体

public static void quickSort3(int[] arr, int left, int right) {
//记录真实的左右边界 而left right是一个会变动的值
int trueLeft = left;
int trueRight = right;
if (right - left < 20)
{
//如果数组已经变得足够短,可以使用插入排序,在小数组的情况下,插入排序的效率优于快排
partInsertSort(arr,left,right);
return;
}

if (left < right) {
//获取枢纽值,并将其放在当前待处理序列末尾
dealPivot(arr, left, right);
//枢纽值被放在序列末尾
int pivot = right - 1;
//左指针
int i = left;
//右指针
int j = right - 1;
int leftLen = 0;
int rightLen = 0;
while (i < j) {
while (arr[i] <= arr[pivot]) {
//当与中轴相等的情况 把元素放到最左边 待处理
if(arr[i] == arr[pivot]){
leftLen++;
swap(arr,left,i);
left++;
}
i++;
}
while (j > left && arr[j] >= arr[pivot]) {
//当与中轴相等的情况 把元素放到最右边 待处理
if(arr[j] == arr[pivot]){
rightLen++;
swap(arr,right,i);
right--;
}
j--;
}
//交换左右元素
if (i < j) {
swap(arr, i, j);
}
}
if (i < right) {
//把中轴放回
swap(arr, i, trueRight - 1);
//处理放在左边的重复元素
for (int k = leftLen; k > 0; k--) {
swap(arr, i - 1 - k, left - k);
}
//处理放在右边的重复元素
for (int k = rightLen; k > 0; k--) {
swap(arr, i - 1 + k, right + k);
}
}
//向左递归
quickSort3(arr, trueLeft, i - 1);
//向右递归
quickSort3(arr, i + 1, trueRight);
}
}

处理数组部分元素的插入排序

/**
* 处理数组中部分元素的插入排序
*/
public static void partInsertSort(int[] arr,int left,int right){
int insertVal,insertIndex;
for(int i = left;i <= right; i++){
insertVal = arr[i];
insertIndex = i - 1;
while(insertIndex >= 0 && insertVal < arr[insertIndex]){
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
}
}