快速排序的优化
平常的快排,我们都会采用固定位置作为基准,一般都会选取第一个和最后一个,但是这种方法在数组原本就有序的时候,反而处理起来会变为冒泡排序,更麻烦了。所以我们要尝试着将基准的位置选取避免掉这种情况,从而达到优化的效果。
一、随机选取基准法
1.基本思想
在[low,high]区间随机选取一个位置,将该位置的值与low位置进行交换后,再找基准排序。
2.实现
/*
快排优化1:随机取基准
*/
public static void RandomSort(int[] array,int low,int high){
Random random = new Random();
//取得【low,high】区间上的任意值
int rand = random.nextInt(high-low)+low;
//将rand和low进行交换
int tmp = array[rand];
array[rand] = array[low];
array[low] = array[rand];
}
3.时间对比
对未优化和优化后排序时间进行比对如图
可以看到两者相差35ms,但是随机取基准的优化因为基准的不确定性,所以其优化效果也并不稳定,甚至有的时候还会“弄巧成拙”,反而降低速度。针对这个问题,又有了三分取中法。
二、三分取中
1.算法思想
(1)当快速排序的数组本身为有序时,进行取基准以后基准的左右两边长度差异较大,反而时间复杂度会增加,基本等同于冒泡排序。
(2)为了避免这种本身有序带来的时间复杂度变高的问题,取最前面的low和最后的high还有最中间的mid三个数,比较大小。
(3)选取第二大的数放在最前面,然后再进行快排。
2.图解
3.实现
/**
* Created with IntelliJ IDEA
*
* @Description:快速三分取中优化
* @Author: zhen
* @Date: 2019/8/18
* @Time: 0:27
*/
public class SortTest1 {
//一次快排找基准
public static int partion(int[] array,int low,int high){
int tmp = array[low];
while(low < high){
while(low < high && array[high] >= tmp){
high --;
}
if(low >= high){
break;
}else{
array[low] = array[high];
}
while(low < high && array[low] <= tmp){
low++;
}
if(low >= high) {
break;
}else{
array[high] = array[low];
}
}
array[low] = tmp;
return low;
}
public static void mindOfThree(int[] array,int low,int high){
int mid =(low+high)/2;
// 6 4 9
//确定结尾值大于中间值
if( array[high] < array[mid] ){
int tmp = array[mid];
array[mid] = array[high];
array[high] = tmp;
//交换
}
//确定结尾大于开始值
if(array[high] < array[low]){
int tmp = array[high];
array[high] = array[low];
array[low] = tmp;
}
//确定开始值大于中间值
if(array[mid] > array[low]){
int tmp = array[mid];
array[mid] = array[low];
array[low] = tmp;
}
//此时将第二大的值放在了low中
}
//分组实现快排
public static void quick(int[] array,int start,int end){
//三分取中
mindOfThree(array,start,end);
//找基准
int par = partion(array,start,end);
//判断左边是否只剩一个元素
if(par > start+1){
quick(array,start,par-1);
}
if(par < end -1){
quick(array,par+1,end);
}
}
public static void quickSort(int[] array){
quick(array,0,array.length-1);
}
public static void main(String[] args) {
int[] array = new int[10000];
for(int i = 0; i < array.length; i++){
array[i] = i;
}
long startTime = System.currentTimeMillis();
quickSort(array);
long endTime = System.currentTimeMillis();
System.out.println(Arrays.toString(array));
System.out.println("消耗时间:"+(endTime-startTime));
}
}
4.时间比对
排序有10000个数据的数组的时间比对
可以看出三分取中消耗的时间非常短,效率很高。但是数组逐次递归排时,逐渐趋于有序的时候,此时快排的效率就不如插入排序快,为了使排序速度更快,就有了进一步的优化。
三、结合插入排序
1.算法思想:
当待排序数据到一定的大小(大小可以根据自己的实际情况来设置)时,采用插入排序。
2.实现
public static void quick(int[] array,int start,int end){
// 快排优化2:当排序数据小于一个给定的数据,认为逐渐趋于有序,后用直接插入排序
if(end - start +1 <= 16){
insertSort2(array,start,end);
return;
}
medianOfThree(array,start,end);
int par = partion(array,start,end);
//排左边
if(par > start+1){
quick(array,start,par-1);
}
if(par < end-1){
quick(array,par+1,end);
}
}
#### 3.速度对比
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190819011005571.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190819011124370.png)
可以看到确实还是提升了一点速度~