排序算法 | 快速排序算法原理及实现和优化(二)

接上文《数据结构和算法 | 快速排序算法原理及实现和优化(一)》,我们来讲讲快速排序的五种优化方案。

1、优化选取基准点

三数取中法:先找出三个关键字,然后排序,取中间的关键字(至少不会是两个极端)。在代码示例中我们选取相应区间的开始元素、中间元素和末尾元素。

按照所选取的基准点的顺序可以构造一颗顺序二叉树。二叉树的深度就是递归的深度。二叉树的平衡性越好,算法的性能越好。

2、优化不必要的交换

将交换改为赋值。

3、优化小数组时的排序方案

大数据排序时对快速排序算法性能的影响很小。但是,只有几个数据要排序时有效率更高的排序算法。例如,直接插入排序。直接插入排序是简单排序中性能最好的。

4、优化递归操作

递归对于算法的效率有着很大的影响。快速排序算法有两次递归操作,对我们来说太奢侈了,我们应该减少递归的调用,就可以最大程度的提高性能。我们可以利用尾递归

什么是尾递归呢?如果一个函数中递归形式的调用出现在函数的末尾,我们称这个递归函数是尾递归的。

当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活跃记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行玫率会变得更高。

因此,只要有可能我就需要将递归函数写成尾递归的形式。

5、发挥尾递归的优势:处理小部分的的递归

对数据规模较小的部分递归,对数据规模较大的部分通过尾递归的调用分小。尽量把栈的空间利用起来。

具体优化代码

public class QuickSort01 {

    public static int MAX_LENGTH_INSERT_SORT = 3;

    public static void main(String[] args) {
        int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序数组
        sort(array, 0, array.length - 1);
        print(array);
    }

    /**
     * 快速排序
     *
     * @param array 待排序的数组
     * @param low   数组的起始地址
     * @param high  数组的结束地址
     */
    public static void sort(int array[], int low, int high) {
        // 直到两个下标相遇,程序结束
        // ------------优化点3:小规模排序使用直接插入排序------------
        if (high - low > MAX_LENGTH_INSERT_SORT) {
            // ------------优化点4:改为尾递归------------
            while (low < high) {
                // 查找 k 的位置
                int k = partition(array, low, high);
                // ------------优化点5:处理小部分的的递归------------
                if (k - low < high - k) {
                    sort(array, low, k - 1);
                    low = k + 1;
                } else {
                    sort(array, k + 1, high);
                    high = k - 1;
                }
            }
        } else {
            insertSort(array, low, high);
        }
    }

    /**
     * 快速排序,分割的过程
     *
     * @param array 待排序的数组
     * @param low   数组的起始地址
     * @param high  数组的结束地址
     * @return k 值
     */
    public static int partition(int array[], int low, int high) {

        // ------------优化点1:优化选取基准点------------
        selectPoint(array, low, high);

        // 基准点
        int point = array[low];
        // 直到两个下标相遇,程序结束
        while (low < high) {
            // high 向左移动,直至遇到比point值小的记录,停止移动
            while (low < high && array[high] >= point) {
                high--;
            }
            // ------------优化点2:将交换改为赋值------------
            array[low] = array[high];

            //low 向右移动,直至遇到比point值大的记录,停止移动
            while (low < high && array[low] <= point) {
                low++;
            }
            // ------------优化点2:将交换改为赋值------------
            array[high] = array[low];
        }

        // ------------优化点2:别忘记将 point 填回去------------
        array[low] = point;

        return low;
    }

    /**
     * 优化选取基准点:选取相应区间的开始元素、中间元素和末尾元素。将值大小处在中间的元素放到low位置。
     * @param array 待排序的数组
     * @param low  数组区间的开始位置
     * @param high 数组区间的结束位置
     */
    public static void selectPoint(int[] array, int low, int high) {
        int mid = low + (high - low)/2;
        if (array[low] > array[high]) {
            swap(array, low, high);
        }
        if (array[mid] > array[high]) {
            swap(array, mid, high);
        }
        // 经过前面两步,最大值已经在 high 位置
        // 下一步将值大小处在中间的元素放到low位置
        if (array[mid] > array[low]) {
            swap(array, mid, low);
        }
    }

    public static void insertSort(int array[], int low, int high) {
        // 注意:low是数组区间的开始,high是数组区间的结束
        // 循环待排序的部分的数列
        // 第一个数据(下标为low的数据)由于插入排序刚开始,有序表中没有任何记录,可以直接添加到有序表中
        for (int i = low + 1; i < high + 1; i++) {
            int temp = array[i];
            int j = i;
            // 如果前面的元素小于temp,则向后移
            for (; j > low && array[j - 1] > temp; j--) {
                array[j] = array[j - 1];
            }
            // 前一个元素(array[j - 1])和后一个元素(array[j])是相同的
            // 在下一轮时,当array[j - 1]小于或等于temp时,将temp插入array[j](即上一轮的array[j - 1])
            array[j] = temp;
        }
    }

    /**
     * 交换数组中两个元素的位置
     */
    public static void swap(int array[], int low, int high) {
        int temp = array[low];
        array[low] = array[high];
        array[high] = temp;
    }

    /**
     * 打印数组
     */
    public static void print(int array[]) {
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "   ");
        }
        System.out.println();
    }
}

5 1 3 5 9 6

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值