算法-8-归并排序

目录

1、描述

2、特点

3、自顶向下的归并排序

4、自底向上的归并排序

5、归并排序的优化

6、使用场景

7、归并排序是一种渐进最优的基于比较排序的算法。


1、描述

归并排序是基于递归方式进行排序的。它其实可以分为两步:

第一步 递归,(递归就是将数组递归拆分成很小的数组)

第二步,合并。(合并就是将这些很小的数组比较合并成有序的数组)

它的核心思想是一个长度为N的数组,按2的倍数,不断递归拆分成子数组,拆分到最后,每个数组只有两个元素的时候,将子数组a中两个元素比较,交换成有序的子数组,然后和有序的子数组(同等级,长度为2)b合并成c,子数组c这时候有4个元素,然后和他同等级的有序子数组d(长度=4)再合并,以此类推........最后合并成一个有序的完整的数组。

                                         

就那这个图说明,数组a长度是16,递归拆分后,每个数组中的元素都是两个(奇数数组会有剩一个的情况),然后将a[0..1]比较换成有序,然后和a[2..3]合并,再然后将a[0..3]和a[4..7]合并,最后再将a[0..7]和[8..15]合并,这样原数组就排序完成了。

这里忘说了一点,排序中的比较和交换位置,都是在合并的时候完成的。

2、特点

  • 时间复杂度为O( NlogN )
  • 归并排序最 吸引人的性质是它能够保证将任意长度为 N 的数组排序所需时间和 NlogN 成正比;
  • 主要缺点 则是它所需的额外空间和 N 成正比。----------------------------------------------------------------------缺点
  • 应用了高效算法设计当中的分治思想(拆分成小的子数组来排序)

3、自顶向下的归并排序

自顶向下的意思就是将数组从大数组不断拆分子数组,然后进行比较合并的。

public class Merge {
    private static double[] aux;

    public static void sort(double[] a) {
        aux = new double[a.length];
        sort(a, 0, a.length - 1);
    }
    /**
     * 递归拆分
     */
    public static void sort(double[] a, int lo, int hi) {
        if (lo >= hi)
            return;
        int mid = lo + (hi - lo) / 2;
        sort(a, lo, mid);
        sort(a, mid + 1, hi);
        if (a[mid + 1] < a[mid]) {
            merge(a, lo, mid, hi);
        }
    }
    /**
     * 将子数组合并
     */
    private static void merge(double[] a, int lo, int mid, int hi) {

        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k];
        }
        int i = lo;
        int j = mid + 1;

        for (int k = lo; k <= hi; k++) {
            if      (i > mid)          a[k] = aux[j++];//如果i>mid了说明lo-mid之间的元素已经全部合并到a数组中了,剩下的就是mid+1-hi的元素依次放进去就行了
            else if (j > hi)           a[k] = aux[i++];//如果j>mid了,说明mid+1-hi之间的元素已经全部放入a数组中了
            else if (aux[i] < aux[j])  a[k] = aux[i++];//如果mid前面的数组元素比后面的数组元素小,优先把mid前面的元素放进去
            else                       a[k] = aux[j++];//如果mid后面的数组元素比前面的数组元素小,优先放mid后面的元素进入到a中
        }
    }
    private static void exch(double[] a, int i, int j) {
        double temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
    private static void show(double[] a) {
        System.out.println("\n");
        for (double item : a) {
            System.out.print((int)item + ",");
        }
    }
    public static void main(String[] args) {
        double[] a = { 55, 43, 23, 12, 13, 11, 7, 8, 88, 6, 4, 2, 3, 1, 9, 8, 7, 11, 56, 45, 22, 23,
                45, 66 };
        sort(a);
        show(a);
    }
}

上面的代码的拆分和合并的过程如下图:

                                                                       

4、自底向上的归并排序

自底向上的思想就是先将2个2个元素比较换成有序,然后再将4个4个的元素比较换成有序,然后是8个、16个、32个。

public class MergeBU {

    private static double[] aux;

    public static void sort(double[] a ){
        aux=new double[a.length];
        int N=a.length;
        
        for (int t=1;t<N;t=t+t){ //t的值为1,2,4,8,16......
           
            for (int k=0;k<N;k+=t+t){
                merge(a,k,k+t-1,Math.min(k+t+t-1,N-1));
            }
        }
    }
    private static void merge(double[] a, int lo, int mid, int hi) {
        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k];
        }
        int i = lo;
        int j = mid + 1;
        for (int k = lo; k <= hi; k++) {
            if (i > mid)               a[k] = aux[j++];
            else if (j > hi)           a[k] = aux[i++];
            else if (aux[i] < aux[j])  a[k] = aux[i++];
            else                       a[k] = aux[j++];
        }
    }

    private static void exch(double[] a, int i, int j) {
        double temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
    private static void show(double[] a) {
        System.out.println("\n");
        for (double item : a) {
            System.out.print(item + ",");
        }
    }
    public static void main(String[] args) {
        double[] a = { 55, 43, 23, 12, 13, 11, 7, 8, 88, 6, 4, 2, 3, 1, 9, 8, 7, 11, 56, 45, 22, 23,
                45, 66 };
        sort(a);
        show(a);
    }
}

5、归并排序的优化

我们的优化从三方面入手:

1、如果递归拆分的数组长度小于7(建议取值5~15)的话,我们采用插入排序来加快排序的速度。下图绿色框

2、将拷贝数组aux[]作为参数穿进去。这样能节省每次merge时,拷贝数组的时间(空间消耗没减小)。下图红色框

3、当a[0..1]和a[2..3]合并的时候,如果a[1]比a[2]小,说明这两个数组的顺序是依次排的,这样就可以减少一次合并。下图蓝色框

                                        

60000条数据的话,优化后要比原始的归并排序快了0.8倍。

For 60000 random Doubles
  全部优化 is 0.8 times faster than 从顶向下归并排序
For 60000 random Doubles
  快速排序 is 0.7 times faster than 从顶向下归并排序
For 60000 random Doubles
  希尔排序 is 1.2 times faster than 从顶向下归并排序
For 60000 random Doubles
  插入排序 is 123.4 times faster than 从顶向下归并排序

6、使用场景

  1. 对于长度为 N 的任意数组,自顶向下的归并排序需要 1⁄2NlgN 至 NlgN 次比较。
  2. 对于长度为 N 的任意数组,自顶向下的归并排序最多需要访问数组 6NlgN 次

适用场景1:上面的1和2点说明了归并排序能够保证将任意长度为 N 的数组排序所需时间和 NlogN 成正比,这样让它拥有了可以处理数百万甚至更大的数组排序。但是缺点辅助数组aux导致的额外空间和N的大小成正比。

适用场景2:自底向上的归并排序比较适合用链表组织的数据。想象一下将链表先按大小为 1 的子链表进行 排序,然后是大小为 2 的子链表,然后是大小为 4 的子链表等。这种方法只需要重新组织链表链接 就能将链表原地排序(不需要创建任何新的链表结点)。

7、归并排序是一种渐进最优的基于比较排序的算法。

下面命题 I 说明没有任何排序算法能够用少于 ~NlgN 次比较将数组排序,这是其他排序算法复杂度的下限。归并排序在最坏情况下的比较次数和任意基于比较的其他排序算法所需的最少比较次数都是 ~NlgN。

                                         

                                         

8、归并排序的时间复杂度计算

                                        

                                        

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 算法10-12~10-14是关于归并排序的。 归并排序是一种基于分治思想的排序算法,它将待排序的序列分成两个序列,对每个序列进行排序,然后将两个已排序序列合并成一个有序序列。 算法10-12是归并排序的递归实现,它将待排序的序列不断地分成两个序列,直到每个序列只有一个元素,然后将这些序列两两合并,直到最终得到一个有序序列。 算法10-13是归并排序的非递归实现,它使用迭代的方式将待排序的序列分成若干个序列,然后将相邻的序列两两合并,直到最终得到一个有序序列。 算法10-14是归并排序的优化实现,它使用插入排序来处理长度较小的序列,以提高排序效率。具体来说,当序列的长度小于等于一定阈值时,使用插入排序排序,否则使用归并排序排序。 ### 回答2: 归并排序是一种基于分治思想的排序算法。该算法的核心思想是将待排序的序列不断分割成更小的序列,直到每个序列只有一个元素,然后将这些序列逐一合并,直到整个序列有序。因此,归并排序分为两个主要过程,分别是分割过程和合并过程。 分割过程:归并排序首先将待排序的序列均分成两个序列,然后递归地对序列进行分割,直到每个序列只有一个元素。 合并过程:将两个有序的序列合并成一个有序的序列。此时需要定义两个指针 i 和 j 分别指向两个序列的起始位置,比较两个指针所指向的元素的大小,将较小的元素放到结果数组中并将指针向后移动,直到有一个序列的指针到达了序列的末尾,然后将另一个序列中未处理的元素依次放入结果数组中。 归并排序的时间复杂度为 O(N*logN),其中 N 为序列的长度。因此,归并排序是一种性能较好且稳定的排序算法,但由于其需要使用临时数组来辅助排序,因此空间复杂度为 O(N)。 总之,归并排序是一种高效且稳定的排序算法,适用于各种规模的序列。在实际应用中,可以使用多线程或并发编程来加速归并排序的执行过程,提高排序效率。 ### 回答3: 归并排序是一种常见的排序算法,采用了分治的思想,可以在最坏情况下也达到O(nlogn)的时间复杂度。它将一个大问题拆分成小问题,然后逐个解决小问题。下面是归并排序的基本流程: 1. 将待排序的序列按照中间位置分为两个序列,分别排序。 2. 合并两个有序的序列,形成一个新的有序序列。 具体实现时,我们可以使用递归或迭代两种方式。下面以递归方式来说明归并排序的实现。 算法10-12:递归实现归并排序 1. 将序列按中间位置分为左右两个序列。 2. 对左右序列分别递归调用归并排序。 3. 合并左右序列。 算法10-13:合并两个有序序列 1. 定义一个新序列,长度为左右序列之和。 2. 从两个序列的头开始比较,将小的元素放入新序列中。 3. 将剩余的元素全部复制到新序列中。 算法10-14:归并排序时间复杂度 1. 分解阶段:将序列分为两个序列,时间复杂度为O(logn)。 2. 合并阶段:合并两个有序序列,时间复杂度为O(n)。 3. 总时间复杂度为O(nlogn)。 归并排序虽然时间复杂度较低,但空间复杂度为O(n),需要额外的存储空间来存储临时序列。但相比其他排序算法归并排序具有稳定性,适合处理大规模数据的排序

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值