数据结构和算法八-排序算法优化

1 切换到插入排序

对于小数组,递归类型的排序比插入排序要慢。当递归到某个范围内的小数组时切换到插入排序性能会有良好的提高。经验表明,这个范围在5~15的时候效果良好

1.1 快速排序优化

public class QuickWithInsertSort extends BaseQuickSort {

    //阙值
    private final int CUTOFF = 10;

    @Override
    protected void quickSort(Comparable[] arrs, int left, int right) {
        if (left + CUTOFF >= right){
            // 如果切分到某个阈值内的小数组用插入排序
            insertSort(arrs, left, right);
            return;
        }
        int j = partition(arrs, left, right);
        quickSort(arrs, left, j - 1);
        quickSort(arrs, j + 1, right);
    }
}

1.2 归并排序优化

public class MergeWithInsertSort extends BaseMergeSort {

    // 阙值
    private final int CUTOFF = 7;

    @Override
    protected void mergeSort(Comparable[] arr, int left, int right, Comparable[] temp) {
        if (left < right) {
            //当数组长度达到阙值时,使用插入排序
            if (right <= left + CUTOFF) {
                insertSort(arr, left, right);
                return;
            }
            int mid = (left + right) / 2;
            //左边递归分解,使得左子序列有序
            mergeSort(arr, left, mid, temp);
            //右边递归分解,使得右子序列有序
            mergeSort(arr, mid + 1, right, temp);
            //将两个有序子数组合并操作
            merge(arr, left, mid, right, temp);
        }
    }
}

2 双端比较归并排序

这种排序不会优化归并排序,只是归并排序的另一种写法
在merge()方法中的归并过程需要判断i和j是否已经越界,即某半边已经用尽。可以用另一种方式,去掉检测是否某半边已经用尽的代码。
具体步骤是将数组arr[]的后半部分以降序的方式复制到temp[],然后从两端归并。对于数组{1,2,3}和{2,3,5},第一个子数组照常复制,
第二个则从后往前复制,最终temp[]中的元素为{1,2,3,5,3,2}。这种方法的缺点是使得归并排序变为不稳定排序。
然后从两端依次比较元素,把较小的元素放到数组中
代码实现

public class BinaryWithInsertMergeSort extends MergeWithInsertSort {

    @Override
    protected void merge(Comparable[] arr, int left, int mid, int right, Comparable[] temp) {
        // 在merge()方法中的归并过程需要判断i和j是否已经越界,即某半边已经用尽。可以用另一种方式,去掉检测是否某半边已经用尽的代码。
        // 具体步骤是将数组arr[]的后半部分以降序的方式复制到temp[],然后从两端归并。对于数组{1,2,3}和{2,3,5},第一个子数组照常复制,
        // 第二个则从后往前复制,最终temp[]中的元素为{1,2,3,5,3,2}。这种方法的缺点是使得归并排序变为不稳定排序。
        System.arraycopy(arr, left, temp, left, mid + 1 - left);
        for (int j = mid + 1; j <= right; j++) {
            temp[j] = arr[right - j + mid + 1];
        }
        int i = left;
        int j = right;
        for (int k = left; k <= right; k++) {
            if (less(temp[i], temp[j])) {
                arr[k] = temp[j --];
            } else {
                arr[k] = temp[i ++];
            }
        }
    }
}

3 三数取中快速排序

传统的快速排序中,基是随机选取的。如果一个数组原本是从大到小排序的,则会导致左右递归次数不均。基准越接近中位数,递归效果越好。
优化方式是在切分之前把小数组左、中、右元素按照中、小、大排序即可
代码实现

public class MedianQuickSort extends BaseQuickSort {

    //阙值
    private final int CUTOFF = 7;

    @Override
    protected void quickSort(Comparable[] arrs, int left, int right) {
        if (left + CUTOFF >= right){
            insertSort(arrs, left, right);
            return;
        }
        partitionMedianOfThree(arrs, left, right);
        int j = partition(arrs, left, right);
        quickSort(arrs, left, j - 1);
        quickSort(arrs, j + 1, right);
    }

    private void partitionMedianOfThree(Comparable[] arrs, int left, int right) {
        int mid = left + (right + -left) / 2;
        // 把左、中、右元素按照中、小、大排序
        if (less(arrs[mid], arrs[right])) {
            exch(arrs, mid, right);
        }
        if (less(arrs[left], arrs[right])) {
            exch(arrs, left, right);
        }
        if (less(arrs[mid], arrs[left])) {
            exch(arrs, mid, left);
        }
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值