35-咸鱼学Java-快速排序的几种优化方式

快速排序介绍
在此文章之前请先阅读快速排序
快速排序的优化主要是再基准的选择上和尽量减少partition的运行。
其基准选择有几种方法1.随机选择法2.三分取中法,等等。
减少partition的方法主要输相同元素聚合。
其实现方法为

1.随机选择法

原来的选择基准为一直选择low下标的元素,所以在处理某些大量有序的序列的时候就会造成大量的元素在一侧,使得快排没有任何效果。
此时只需要在快排前面加入一段代码。

//产生start - end 的随机数
int randomNum=  (int)(Math.random()*(end-start)+start);
//交换start下标和随机位置的两个数,因为我们的partition默认取的是
//start位置所以可以通过换位置实现以随机位置为基准。
swap(array, start,randomNum);

2.三分取中法

加入一个函数

    /**
     * 三分取中
     * @param array 数组
     * @param low   下限
     * @param hight 上限
     */
    public static void  SelectThreeMid(int[] array,int low,int hight)
    {
        int mid = (low+hight)>>>1;
        if(array[low]<array[mid])   //目标low>mid
        {
            swap(array, mid, low);
        }
        if(array[hight]<array[mid]) //目标hight>mid
        {
            swap(array, hight, mid);
        }
        if(array[low]>array[hight]) //目标low<hight
        {
            swap(array, low, hight);
        }
        //最终结果为 low 下标的为中间值
    }

只需要将这行代码插入到partition执行之前,此代码就会将三个数中中间的数放入到下限下标,然后partition就会用下限下标来作为标准快排。

3.同元素聚合法

同元素聚合及为当以某个元素当做partition的基准的时候,顺便将和其相同的元素聚合在一起然后再进行partition。
函数

    /**
     * 聚集和基准相同的元素
     * @param array 数组
     * @param start 开始下标
     * @param end   结束下标
     * @param par   基准
     */
    private static int[] focus(int[] array, int start, int end, int par) {
        //查找的范围
        int left = par-1;
        int right = par+1;
        //交换的指引变量
        int parLeft = par-1;
        int parRight = par+1;
        //左边找相同的
        for (int i = left; i >=start; i--) {
            //当找到相同的
            if(array[i] == array[par])
            {
                //如果不相邻
                if(i!=parLeft)
                {   //i和交换的指引下标进行交换
                    swap(array, parLeft, i);
                    //指引下标移动
                    parLeft--;
                }
                else
                {   //如果相邻,只移动指引下标
                    parLeft--;
                }

            }

        }
        //右边找 原理同上
        for (int i = right; i <=end; i++) {
            if(array[i] == array[par])
            {
                if(i!=parRight)
                {
                    swap(array, parRight, i);
                    parRight++;
                }
                else
                {
                    parRight++;
                }
            }
        }
        //new一个数组,放入聚合在一起元素的上下限,及下次快排应该越过其,只在其上下限进行排序
        int [] focus = {parLeft,parRight};
        return focus;
    }

这个要对快速排序的函数进行改造

public static void Quick(int[] array,int start,int end)
    {   
        //先找到基准下标
        int par = partion(array, start, end);
        //调用聚集函数,聚集,找到应该快排的区段
        int[] focus = focus(array,start,end,par);
        int left = focus[0];
        int right = focus[1];
        //判断基准的左边是不是还有1个以上元素,如果是再调用快排
        if(par>start+1)
        {    
            Quick(array, start, left);
        }
        //判断基准的右边是不是还有1个以上元素,如果是再调用快排
        if(par<end-1)
        {   
            Quick(array, right, end);
        }
    }

4.当数据量小到某个节点的使用使用快排

快排函数

/**
     * 给某个区段快速排序 [start,end]
     * @param array
     * @param start
     * @param end
     */
    private static void insert_sort(int[] array,int start,int end) {
        int temp = 0;
        int j = 0;
        for (int i = start+1; i < end+1; i++) {
            temp = array[i];
            for (j = i-1; j >= 0 ; j--) {
                if(array[j]>temp){
                array[j+1] = array[j];
                }else
                {
                    break;
                }
            }
            array[j+1] = temp;
        }
    }

快排函数改造
在快排函数的第一行加上

    if(end-start+1<num)
    {
        insert_sort(array,start,end);
        return;
    }

此函数可以实现在快排的数量级小于num的时候实现插入排序。

最佳方案

以上方案有的可以叠加使用,当然效果是最好的
查阅资料可知,以上方法的组合,目前最快的可能是三数取中+聚集+插入排序

猜你喜欢

转载自blog.csdn.net/qq_38345606/article/details/80358828
今日推荐