快速排序(Quick Sort)
快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的两部分其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
快速排序优化
1.优化选取枢轴
三数取中(median-of-three)法。即取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端、和中间三个数。
也可以随机选取,由于整个序列是无序的状态,随机选取三个数和从左中右端取三个数其实是一回事,而且随机数生成器本身还会带来时间上的开销,因此随机产生不予考虑
2.优化不必要的交换
3.优化小数组时的排序方案
当数组非常小,快速排序反而不如直接插入排序来得更好(直接插入排序是简单排序中性能最好的)。其原因在于快速排序用到了递归操作。
4.优化递归操作
import java.util.Arrays;
import java.util.Random;
/**
* 快速排序及其优化
*
*/
public class QuickSort {
/**
* 数组长度阀值
*/
private static final int MAX_LENGHT_INSERT_SORT = 7;
/**快速排序
* @param arr
*/
public void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
private void quickSort(int[] arr, int low, int high) {
int pivot;
/** 优化三:优化小数组时的排序方案*/
if ((high - low) > MAX_LENGHT_INSERT_SORT) {
/** 优化四:优化递归操作 实施尾递归优化*/
// if (low < high) { // 此处优化三已被优化三同化
// pivot = partition(arr, low, high);
// quickSort(arr, low, pivot - 1);
// quickSort(arr, pivot + 1, high);
// }
/**
* 当我们将if改为while后,因为第一次递归以后,变量low就没有用处了,
* 所以可以将pivot + 1赋值给low,再循环后,来一次partition(arr, low, high),
* 其效果等同于 quickSort(arr, pivot + 1, high)。
* 结果相同,但因采用迭代而不是递归的方法可以缩减堆栈深度,从而提高了整体性能
*/
while (low < high) {
pivot = partition(arr, low, high);
quickSort(arr, low, pivot - 1);
low = pivot + 1;
}
} else {
insertSort(arr);
}
}
/**
* 交换数组中记录,使枢轴记录到位,并返回其所在位置
* 此时在它之前(后)的记录均不大(小)于它
* @param arr
* @param low
* @param high
* @return
*/
private int partition(int[] arr, int low, int high) {
/** 优化一:优化选取枢轴 三数取中 begin*/
//计算数组中间的元素的下标
int m = (high - low)/2 + low;
// 交换左边和右边的数据,保证左边的小
if (arr[low] > arr[high])
swap(arr, low, high);
// 交换中间和右边的数据,保证中间的小
if (arr[m] > arr[high])
swap(arr, m, high);
// 交换左边和中间的数据,保证左边的最小(注意最小)
if (arr[low] > arr[m])
swap(arr, low, m);
/** 优化一:优化选取枢轴 end*/
// 用子表的第一个记录作枢轴记录
int pivotKey = arr[low];
// 最终使得枢纽之前(后)的记录均不大(小)于它
while (low < high) {
while (low < high && arr[high] >= pivotKey)
high--;
// 将比枢轴记录小的记录交换到低端
// swap(arr, low, high);
/**优化二:优化不必要的交换*/
arr[low] = arr[high];
while (low < high && arr[low] <= pivotKey)
low++;
// 将比枢轴大的记录交换到高端
//swap(arr, low, high);
arr[high] = arr[low];
}
/**优化二:优化不必要的交换*/
// 将枢轴数值替换回 low的位置
arr[low] = pivotKey;
return low;
}
private void swap(int[] arr, int m, int n) {
int temp;
temp = arr[m];
arr[m] = arr[n];
arr[n] = temp;
}
/**
* 直接插入排序
* @param arr
*/
private void insertSort(int[] arr) {
int k = 0;
for (int i = 1; i < arr.length; i++) {
if (i < arr[i - 1]) {
// 放入哨兵点
k = arr[i];
int j;
for (j = i - 1; j >= 0 && arr[j] > k; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = k;
}
}
}
public static void main(String[] args) {
// int[] arr = {9, 1, 5, 8, 3, 7, 4, 6, 2};
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = new Random().nextInt(50);
}
System.out.println(Arrays.toString(arr));
new QuickSort().quickSort(arr);
}
}
无优化版本
import java.util.Arrays;
import java.util.Random;
/**
* 快速排序无优化版
*
*/
public class QuickSort {
/**快速排序
* @param arr
*/
public void quickSort(int[] arr) {
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
private void quickSort(int[] arr, int low, int high) {
int pivot;
if (low < high) {
pivot = partition(arr, low, high);
quickSort(arr, low, pivot - 1);
quickSort(arr, pivot + 1, high);
}
}
/**
* 交换数组中记录,使枢轴记录到位,并返回其所在位置
* 此时在它之前(后)的记录均不大(小)于它
* @param arr
* @param low
* @param high
* @return
*/
private int partition(int[] arr, int low, int high) {
// 用子表的第一个记录作枢轴记录
int pivotKey = arr[low];
// 最终使得枢纽之前(后)的记录均不大(小)于它
while (low < high) {
while (low < high && arr[high] >= pivotKey)
high--;
// 将比枢轴记录小的记录交换到低端
swap(arr, low, high);
while (low < high && arr[low] <= pivotKey)
low++;
// 将比枢轴大的记录交换到高端
swap(arr, low, high);
}
return low;
}
private void swap(int[] arr, int m, int n) {
int temp;
temp = arr[m];
arr[m] = arr[n];
arr[n] = temp;
}
public static void main(String[] args) {
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = new Random().nextInt(50);
}
System.out.println(Arrays.toString(arr));
new QuickSort().quickSort(arr);
}
}