排序算法(六)-归并排序

0.原理

归并排序的基本思想是分治法。先将无序序列分为若干个无序子序列,然后对无序子序列进行排序,最后合并有序子序列,得到完全有序的序列。这就是分解,求解,合并的过程。

  1. 将待排序序列data[0,1,2….n-1]看成是n个长度为一的序列,将相邻序列进行合并,得到n/2个长度为2的有序序列;
  2. 将相邻序列再次进行合并,得到n/4个长度为4的有序序列
  3. 重复第一步和第二步,直到得到长度为n的一个有序序列,此时的序列为已排序序列

关键就在于如何合并序列,每次合并的过程都是对两个有序序列进行合并,由于这两个是相邻的,因此我们可以将两个序列表示为data[start, mid], data[mid + 1, end]。我们需要一个end-start+1长度的temp[]辅助数据配合合并。

  1. 从data[start, mid]和data[mid + 1, end]中各取出一个数字进行比较,将较小的数字放入到temp中,并且将数字对应的数组游标加一
  2. 重复第一步,知道其中一个数组遍历完
  3. 将另外一个未遍历完的数组直接填入temp末尾
  4. 将temp拷贝回data[start, end]中

依旧对[2 8 1 5 3]这个数组进行模拟,得到下面的结果:

gap=1 start
2 8 1 5 3 
2 8 1 5 3 
gap=1 finish
gap=2 start
2 8 1 5 3 
1 2 5 8 3 
gap=2 finish
gap=4 start
1 2 5 8 3 
1 2 3 5 8 
gap=4 finish
  • 第一个循环将每个长度为1的子序列合并,因为(2,8)(1, 5)都是正确的序列,因此没有变化。
  • 第二个循环将每个长度为2的子序列合并,(2, 8)(1, 5)合并后得到(1, 2, 5, 8)。
  • 第三个循环将每个长度为4的子序列合并,(1, 2, 5, 8)和(3)合并后得到最终结果(1, 2, 3, 5, 8)

1. 实现

@Override
public int[] sort(int[] data) {
    if (data == null || data.length <= 1) {
        return data;
    }

    for (int gap = 1; gap < data.length; gap *= 2) {
        System.out.println(String.format(Locale.CHINA, "gap=%d start", gap));
        printlnArray(data);
        int i;
        for (i = 0; i + 2 * gap - 1 < data.length; i = i + 2 * gap) {
            merge(data, i, i + gap - 1, i + 2 * gap - 1);
        }

        if (i + gap - 1 < data.length) {
            merge(data, i, i + gap - 1, data.length - 1);
        }
        printlnArray(data);
        System.out.println(String.format(Locale.CHINA, "gap=%d finish", gap));
    }

    return data;
}

merge方法没有贴上来,具体实现可看

GitHub/MergeSort

注意,如果最后一个子序列不能正好整除的时候需要特殊处理,将其和倒数第二个子序列合并。

2. 复杂度

归并排序是一种稳定的排序算法,平均,最坏和最好的时间复杂度都为O(nlogn)。但是由于其需要O(n)的辅助空间,因此在一些对内存敏感的机器上,或者数据特别大时有所限制。

从稳定性来说,归并排序相对于快速排序来说是一个既快又稳定的排序方式。但是如果综合考虑到空间和性能来说,快速排序是优于归并排序的。

3. 优化

  1. 对于小数组可使用插入或者选择排序,不必使用合并的方式。
  2. 合并之前如果data[middle] < data[middle + 1]可以不需要合并直接返回。因为左边子序列的最大值小于右边子序列的最小值。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值