归并排序的Java实现

归并排序的Java实现

归并排序的思想就是将数组拆分成两部分,分别进行排序,然后再归并起来。它运用了分而治之的思想。它的时间复杂度只有 O ( N l o g N ) O(NlogN) O(NlogN)

由此引出两种归并排序方式:

1、自顶向下归并排序,使用递归从上往下拆分可以完成

2、自底向上归并排序,先归并微型数组,再归并得到的数组以此合并成排序好的数组。

无优化版本

以下的版本是没有做任何优化的归并排序方式

import java.util.Arrays;
import java.util.Random;

/**
 * 归并排序
 *
 * @Author Nino 2019/10/3
 */
public class MergeSort<E extends Comparable<E>>{

    /**辅助数组,存储没合并前的数组 */
    private E[] aux;

    /**
     * 合并过程
     *             arr/aux
     * 2   4   5   7 | 3   1   6   8
     * l           m               r
     *
     * @param arr 需要被合并的数组
     * @param l   数组的最左索引
     * @param m   数组的中间分隔索引(m + 1为第二个数组的起始索引)
     * @param r   数组的最右索引
     */
    private void merge(E[] arr, int l, int m, int r) {
        //给辅助数组赋值
        for (int k = l; k <= r; k++) {
            aux[k] = arr[k];
        }
        //左数组的指针
        int i = l;
        //右数组的指针
        int j = m + 1;

        //arr的指针定为k,将左数组和右数组合并到arr[l, r]中
        for (int k = l; k <= r; k++) {
            //左数组指针越界,将右数组剩余元素放进arr
            if (i > m) {
                arr[k] = aux[j++];
            }
            //右数组指针越界,将左数组剩余元素放进arr
            else if (j > r) {
                arr[k] = aux[i++];
            }
            //比较左右数组指针所指位置的大小
            else if (aux[i].compareTo(aux[j]) <= 0) {
                arr[k] = aux[i++];
            } else {
                arr[k] = aux[j++];
            }
        }
    }

    /**
     * 自顶向下归并排序
     * 递归接口
     * @param arr
     */
    public void up2DownSort(E[] arr) {
        //给辅助数组定空间大小(注意这里需要可比较性)
        aux = (E[]) new Comparable[arr.length];
        up2DownSort(arr, 0, arr.length - 1);
    }

    /**
     * 自顶向下归并排序
     * 递归实现
     * @param arr 数组
     * @param l   左边界
     * @param r   右边界
     */
    private void up2DownSort(E[] arr, int l, int r) {
        if (l == r) {
            return;
        }
        
        int mid = l + (r - l) / 2;
        up2DownSort(arr, l, mid);
        up2DownSort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }

    /**
     * 自底向上归并排序
     * 先归并微型数组,再归并得到的微型数组
     *
     * @param arr
     */
    public void down2UpSort(E[] arr) {
        int N = arr.length;
        aux = (E[]) new Comparable[N];
        
        //sz为微型数组大小
        for (int sz = 1; sz < N; sz *= 2) {
            for (int i = 0; i < N - sz; i += sz + sz) {
                // 对 arr[i -> i+sz-1] 和 arr[i+sz -> i+2*sz-1] 进行归并
                merge(arr, i, i + sz - 1, Math.min(i + sz + sz - 1, N - 1));
            }
        }
    }

    /**
     * 测试用例
     *
     * @param args
     */
    public static void main(String[] args) {
        Random random = new Random();
        Integer[] arr = new Integer[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = Integer.valueOf(random.nextInt(20));
        }
        System.out.println(Arrays.asList(arr).toString());
        new MergeSort<Integer>().up2DownSort(arr);
        System.out.println(Arrays.asList(arr).toString());
    }
}

优化版本

优化的方法在于两点:

1、当数组被拆分成很小数组的时候,使用插入排序

2、当两个要合并的数组的边界两个元素满足有序时不进行合并操作

一般情况下,只采用第一种优化方案。

需要写一个插入排序的静态方法,以及更改以下递归函数自底向上的方法就可以了

	/**
     * 对arr[l...r]的区间使用InsertionSort排序
     * @param arr
     * @param l
     * @param r
     */
    public static void insertionSort(Comparable[] arr, int l, int r){

        for( int i = l + 1 ; i <= r ; i ++ ){
            Comparable e = arr[i];
            int j = i;
            for( ; j > l && arr[j-1].compareTo(e) > 0 ; j--)
                arr[j] = arr[j-1];
            arr[j] = e;
        }
    }

	/**
     * 自顶向下归并排序(优化方案)
     * 递归实现
     * @param arr 数组
     * @param l   左边界
     * @param r   右边界
     */
    private void up2DownSort(E[] arr, int l, int r) {
       	//使用优化方案时,这个部分可以省略
        if (l == r) {
            return;
        }

        //优化方案,对于小规模数组, 使用插入排序
        if( r - l <= 15 ){
            insertionSort(arr, l, r);
            return;
        }

        int mid = l + (r - l) / 2;
        up2DownSort(arr, l, mid);
        up2DownSort(arr, mid + 1, r);
        merge(arr, l, mid, r);
    }

	/**
     * 自底向上归并排序(优化方案)
     * 先归并微型数组,再归并得到的微型数组
     *
     * @param arr
     */
    public void down2UpSort(E[] arr) {
        int N = arr.length;
        aux = (E[]) new Comparable[N];
        
        // Merge Sort Bottom Up 优化
        // 对于小数组, 使用插入排序优化
        for( int i = 0 ; i < N ; i += 16 ){
            InsertionSort.sort(arr, i, Math.min(i+15, N-1) );
        }
        //sz为微型数组大小
        for (int sz = 16; sz < N; sz *= 2) {
            for (int i = 0; i < N - sz; i += sz + sz) {
                // 对 arr[i -> i+sz-1] 和 arr[i+sz -> i+2*sz-1] 进行归并
                merge(arr, i, i + sz - 1, Math.min(i + sz + sz - 1, N - 1));
            }
        }
    }

如有错误,欢迎指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值