排序算法【4】--快速排序的两种优化

快速排序的优化

平常的快排,我们都会采用固定位置作为基准,一般都会选取第一个和最后一个,但是这种方法在数组原本就有序的时候,反而处理起来会变为冒泡排序,更麻烦了。所以我们要尝试着将基准的位置选取避免掉这种情况,从而达到优化的效果。

一、随机选取基准法

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)
可以看到确实还是提升了一点速度~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值