精华内容
下载资源
问答
  • 快速排序:理想时间复杂度为:O(nlog2n),最坏情况下时间复杂度为:O(n^2) 堆排序:时间复杂度为:O(nlogn) 冒泡排序:O(n^2) 选择排序:时间复杂度:O(n^2) 插入排序:时间复杂:O(n^2) 归并排序时间复杂度:O...

    快速排序:理想时间复杂度为:O(nlog2n),最坏情况下时间复杂度为:O(n^2)
    堆排序:时间复杂度为:O(nlogn)
    冒泡排序:O(n^2)
    选择排序:时间复杂度:O(n^2)
    插入排序:时间复杂:O(n^2)
    归并排序:时间复杂度:O(nlogn)

    展开全文
  • 归并排序也被认为是一种时间复杂度最优算法,我们还是按照基本过程,代码,最坏时间复杂度,平均时间复杂度,性能分析和改进这几个方面来展开讲述,为什么归并排序被认为是最优基于比较排序算法(理论上)。...

    6cd2331b4f22276975bd7359e28eafa1.png

    前言

    上一篇文章,介绍过第一种基于分治策略的排序算法--快速排序。接下来我们来讨论另一种基于分治策略的排序算法,归并排序。归并排序也被认为是一种时间复杂度最优的算法,我们还是按照基本过程,代码,最坏时间复杂度,平均时间复杂度,性能分析和改进这几个方面来展开讲述,为什么归并排序被认为是最优的基于比较的排序算法(理论上)。

    一、归并排序过程

    7fe254ccc4f2045d9542865f7f2d8df0.gif
    归并排序过程--图片来源:菜鸟教程

    归并排序的思想:

    将待排序序列划分成若干个有序子序列;将两个或两个以上的有序子序列合并为一个有序序列。

    我们大体上有两种解决方案:

    1. 朴素归并排序

    11d4daf7a49e981b60c1a38ce0287893.png

    就像上面图片所展示的那样,先分再治。上面的图片已经非常形象的阐明了归并排序的整体过程。

    上面这种过程的代码如下所示:

        public void Merge(int[] E, int s, int m, int t) {
            int[] B = new int[E.length];
            int k, p, j = 0;
            for (k = s, p = s, j = m + 1; p <= m && j <= t; ++k) {
                if (E[p] <= E[j]) {
                    B[k] = E[p++];
                } else B[k] = E[j++];
            }
            if (p <= m) {
                for (int i = p; i <= m; i++) {
                    B[k++] = E[i];
                }
            }
            if (j <= t) {
                for (int i = j; i <= t; i++) {
                    B[k++] = E[i];
                }
            }
            //写回原数组
            for (p = s; p <= t; p++) E[p] = B[p];
        }
    
        public void Msort(int[] E, int s, int t) {
            if (s < t) {
                int m = 0;
                m = (s + t) / 2;
                Msort(E, s, m);
                Msort(E, m + 1, t);
                this.Merge(E, s, m, t);
            }
        }

    上面这种实现方式就是先不断划分子问题,然后解决最小规模问题,再进行合并。实际上,我们还可以通过第二种方式对这种基本的归并排序算法做一些改进。

    2. 二路归并排序

    其实我们还有另外一种解决方案:将n个待排序的数据直接划分成n个规模为1的子序列。然后进行二路归并排序。

    2cdacc937b9d9a423f63b8c3b39f7a41.png

    二路归并排序直接将n个待排序的数据元素看成n个有序子序列,依次合并两个相邻有序子序列,直到只剩下一个有序子序列为止。

    示例代码如下所示:

    public void Merge2(int[] E, int[] B, int s, int m, int t) {
            int k, p, j = 0;
            for (k = s, p = s, j = m + 1; p <= m && j <= t; ++k) {
                if (E[p] <= E[j]) {
                    B[k] = E[p++];
                } else {
                    B[k] = E[j++];
                }
            }
            if (p <= m) {
                for (int i = p; i <= m; i++) {
                    B[k++] = E[i];
                }
            }
            if (j <= t) {
                for (int i = j; i <= t; i++) {
                    B[k++] = E[i];
                }
            }
            //写回原数组
            for (p = s; p <= t; p++) E[p] = B[p];
        }
        public void Msort_n_re(int[] E, int n){
            int B[] = new int[E.length];
            int len = 1;
            while (len < n) {
                int i;
                for (i = 0; i < n + 1 - 2 * len; i = i + 2 * len) {
                    Merge2(E, B, i, i + len - 1, i + 2 * len - 1);
                }
                if (i + len - 1 < n) {
                    Merge2(E, B, i, i + len - 1, n - 1);
                } else {
                    for (; i < n; i++) E[i] = B[i];
                }
                len *= 2;
            }
        }

    很容易可以看到区别,我们将递归过程改为非递归过程。

    二、归并排序的时间复杂度分析

    归并排序特点:

    • 有序子序列的数据元素的个数≤Merge算法的比较次数
    • Merge算法的比较次数≤2个子序列数据元素个数和-1
    1. 最坏情况时间复杂度

    最坏情况是最后一次比较两个有序子序列各自剩最后一个数据元素。例如

    这个两个子序列合并一共需要比较n-1次,是最坏情况。同理我们也很容易分析出最有情况,就是(1,2,3,4)和(5,6,7,8)这两个序列,只需要比较n/2=8/2=4次即可。

    e37880c6cb519241cbe5e6bab69fc61d.png
    最好情况和最坏情况

    我们基于分治法时间复杂度公式进行分析

    则我们有分析公式如下

    1bcb4d9ef1294fcd66f0d7ea26e37575.png

    则我们可以大胆得出结论,归并排序最坏情况时间复杂度为

    2. 最好情况时间复杂度

    87b1351df30d4af356454a18111f4b88.png

    最好情况时间复杂度也是

    最好情况与最坏情况的时间复杂度都是nlogn量级的,那么我们也很容易得出结论(类比与高等数学的夹逼准则)归并排序的平均时间复杂度也为nlogn量级。

    三、归并排序改进措施

    1. 递归:消除递归,避免递归过程的时间消耗。
    2. 最长无逆序子序列:我们经过分析知道,归并排序的基础是两个有序子序列的合并,那么我们可以通过寻找最长无逆序子序列来优化归并排序的比较次数。例如,(4,5,63,7,1)这个序列,我们找到三个无逆序子序列,直接对这三个子序列进行合并即可,减少比较次数。
    3. 小排序问题:划分为小序列,做直接插入排序,再采用归并排序。
    4. 不回写:这个策略是最重点要讲述的策略。我们上面两段示例代码中都存在从B写回E的操作,这种写回操作在大排序问题时非常浪费时间,我们就思考一种不回写策略来解决这个问题。

    详细过程:

    思路:奇数趟从E[]写到B[]数组,偶数趟从B[]写到E[]数组。如果共做了奇数趟,排序结束,则最多回写一次。

    基于这个思路,我们将二路归并排序的代码做一些小小的修改,以满足我们的不回写策略。这和不回写策略也同时消除了递归过程。下面是示例代码。

    public void Merge1(int[] E, int[] B, int s, int m, int t) {
            int k, p, j = 0;
            for (k = s, p = s, j = m + 1; p <= m && j <= t; ++k) {
                if (E[p] <= E[j]) {
                    B[k] = E[p++];
                } else {
                    B[k] = E[j++];
                }
            }
            if (p <= m) {
                for (int i = p; i <= m; i++) {
                    B[k++] = E[i];
                }
            }
            if (j <= t) {
                for (int i = j; i <= t; i++) {
                    B[k++] = E[i];
                }
            }
            //这里并没有写回操作
        }
    
        public void Msort_no_writeback(int[] E, int n) {
            int B[] = new int[E.length];
            int len = 1;
            int flag = 1;
            while (len < n) {
                if (flag == 1) {//通过flag来判定当前是奇数回合还是偶数回合
                    flag = 0;
                    int i;
                    for (i = 0; i < n + 1 - 2 * len; i = i + 2 * len) {
                        Merge1(E, B, i, i + len - 1, i + 2 * len - 1);
                    }
                    if (i + len - 1 < n) {
                        Merge1(E, B, i, i + len - 1, n - 1);
                    } else {
                        for (; i < n; i++) E[i] = B[i];
                    }
                    len *= 2;
                } else {
                    flag = 1;
                    int i;
                    for (i = 0; i < n + 1 - 2 * len; i = i + 2 * len) {
                        Merge1(B, E, i, i + len - 1, i + 2 * len - 1);
                    }
                    if (i + len - 1 < n) {
                        Merge1(B, E, i, i + len - 1, n - 1);
                    } else {
                        for (; i < n; i++) B[i] = E[i];
                    }
                    len *= 2;
                }
            }
            if (flag == 1) {
                for (int p = 0; p < n; p++) {
                    E[p] = B[p];
                }
            }
        }
    展开全文
  • 因为堆排序时间复杂度为n*logn,空间复杂度为1,是不...而归并排序的时间复杂度为n*logn,空间复杂度为n,是稳定排序。 快速排序的时间复杂度为n,空间复杂度最好的情况是logn,最坏的情况是n^2,是不稳定的排序方法

    因为堆排序时间复杂度为n*logn,空间复杂度为1,是不稳定排序,适合较多情况;
    而归并排序的时间复杂度为n*logn,空间复杂度为n,是稳定排序。
    快速排序的时间复杂度为n,空间复杂度最好的情况是logn,最坏的情况是n^2,是不稳定的排序方法

    展开全文
  • 归并排序及其复杂度分析

    千次阅读 2018-04-19 20:42:27
    * 时间复杂度分析:递归分解数据,需要递归logN次,每次都需要对n个数据扫描一次,最好最坏平均都一样,所以O(nlogn) * 空间复杂度分析:归并排序需要一个临时temp[]来储存归并结果,所以 O(n) * * 基本...

    /*
    * (二分)归并排序(稳定算法)
    * 基本思想:将数组递归分成越来越小的数集,直到每个数集只有一个数
    * 然后将数据递归排序,使其合并成与原来数据一样的有序序列
    * 时间复杂度分析:递归分解数据,需要递归logN次,每次都需要对n个数据扫描一次,最好最坏平均都一样,所以O(nlogn)
    * 空间复杂度分析:归并排序需要一个临时temp[]来储存归并的结果,所以   O(n) 

    * 基本实现:需要两个函数,一个用来递归划分数组,比较简单,依次二分即可
    * 一个用来把划分好的数组递归排序并合并:两个数组从第一个元素开始比较,小的数据入,直到一个数组中数据为空,
    * 然后把另一个数组中的剩余数据写入,最后用temp数组替代初始的数组
    */
     

      public void merge(int[] a, int start, int mid, int end) {
            int[] temp = new int[end - start + 1];
            int i = start;
            int j = mid + 1;
            int k = 0;
            while (i <= mid && j <= end) {
                if (a[i] < a[j]) {
                    temp[k++] = a[i++];
                } else {
                    temp[k++] = a[j++];
                }
            }
            while (i <= mid) {
                temp[k++] = a[i++];
            }
            while (j <= end) {
                temp[k++] = a[j++];
            }
            for (int m = 0; m < temp.length; ++m) {
                a[start + m] = temp[m];
            }
    
        }
    
        /**
         * @param a
         * @param start  数组索引
         * @param end    数组索引,<.length
         */
        public void mergeSort(int a[], int start, int end) {
            int mid = (end + start) / 2;
            if (start < end) {
                mergeSort(a, start, mid);
                mergeSort(a, mid + 1, end);
                merge(a, start, mid, end);
            }
    
        }

    Ps:博文为博主的学习笔记,算法只是按照自己的理解简单分析,初学者建议看详细的图文讲解,如果有错误,欢迎交流指正

     

    展开全文
  • 常见几种排序算法复杂度如下: 方式: 平均 最坏 最好 插入 n^2 n^2 n 希尔 n^1.3 / / 冒泡 n^2 n^2 n 快速 nlogn n^2 nlogn ...其中最好、最坏、平均三项复杂度全是一样就是与初始排序无关的排序...
  • 归并排序 :log2N 通过middle 分开两等分 创建一个空间装进那个空间里面 再赋值回原来空间 #include<stdio.h> #include<stdlib.h> #include<string.h> voidmerge_sort(int*arry,int*...
  • https://jimmee.iteye.com/blog/1985774时间复杂度n^2表示n平方,选择排序有时叫做直接选择排序或简单选择排序排序方法平均时间最好时间最坏时间桶排序(不稳定)O(n)O(n)O(n)基数排序(稳定)O(n)O(n)O(n)归并排序...
  • 排序算法的复杂度

    2020-02-15 16:44:53
    冒泡 排序 算法 时间 复杂度 是 O( n^ ... 快速 排序 是 不稳 定, 最理想 情况下 算法 时间 复杂度 是 O( nlog2n), 最坏 是 O( n^ 2)。 堆 排序 算法 时间 复杂度 是 O( nlogn)。 归并 排序 算法 ...
  • 1常见排序的空间复杂度: O(1):插入排序,选择排序,冒泡排序,堆...希尔排序的平均复杂度是O(nlogn)~O(n2),最好的情况O(n1.3),最坏的情况O(n2) 快速排序的平均复杂度是O(nlogn), 最好的情况O(nlogn),最...
  • 时间复杂度 n^2表示n平方,选择排序有时叫做直接选择排序或简单选择排序 排序方法 平均时间 最好时间 最坏时间 桶排序(不稳定) O(n) O(n) O(n) 基数排序(稳定) O(n) O(n) O(n) 归并排序(稳定) O(nlogn) O(nlogn) O...
  • 涉及排序算法包括:简单选择排序、直接插入排序、希尔排序、归并排序、冒泡排序、快速排序、堆排序、基数排序 时间复杂度:快些以nlogn的速度归队 ...注:快排的最坏情为O(n²),此时待排序的序列为正序或者逆序。
  • 时间复杂度(最坏) 控件复杂度 稳定性 冒泡排序 O(n²) O(n²) O(n) O(1) 稳定 选择排序 O(n²) O(n²) O(n²) O(1) 不稳定 插入排序 O(n²) O(n²) O(n) O(1) 稳定 快速...
  • 1、原址排序算法: 在排序过程中,仅有常数个元素需要存储在待排数组之外,则称该排序算法为原址的。 插入排序:常用于小规模输入数组; 快速排序:常用于大规模数组排序;...2、各排序算法的时间复杂度: ...
  • 排序算法分类 内部排序算法又分为基于比较的排序算法和不基于比较的排序算法,其分类如下: 比较排序: 直接插入排序 希尔排序(插入) 冒泡排序 快速排序 ... 最坏时间复杂度 空间复杂度 是否稳...
  • 方式: 平均 最坏 最好 插入 n^2 n^2 n 希尔 n^1.3 / / 冒泡 n^2 n^2 n 快速 nlogn n^2 nlogn 选择 n^2 n^2 n^2 堆排 nlogn nlogn nlogn ...归并 nlogn nlogn nlogn ...其中最坏为nlogn有 堆排 和 归并
  • 排序算法的复杂度

    2015-04-30 17:11:17
    1.冒泡排序 时间复杂度:O(n^2) 2.选择排序 时间复杂度:O(n^2) ...时间复杂度:最理想情况O(n*log2 n),最坏O(n^2) 5.堆排序 时间复杂度:O(n*log n) 6.归并排序 时间复杂度:O(n*log2 n)
  • (1)冒泡排序的时间复杂度: 1.比较相邻的元素,如果第一个比第二个大(升序),就交换他们两个 2.对每一对相邻的元素做同样的工作,从开始到结尾的最后一对 这步做完后,最后的元素会是最大的数 3.针对所有的...
  • 时间复杂度最坏时间复杂度(最好) 空间复杂度 稳定性 复杂性 插入排序 O(n2) O(n2) O(n) O(1) 稳定 简单 冒泡排序 O(n2) O(n2) O(n) O(1) 不稳定 较复杂 归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n)...
  • 最好:O(n) 最坏:O(n2) 简单选择排序 O(n2) 直接插入排序 O(n2) 改进排序算法 排序方法 时间复杂度 希尔排序 O(n3/2) 堆排序 O(n*logn) 归并排序 O(n*logn) ...
  • 最坏时间复杂度最好时间复杂度平均时间复杂度直接插入 O(n2) O(n) O(n2)简单选择 O(n2) O(n2) O(n2)起泡排序O(n2) O(n) O(n2)快速排序O(n2)O(nlog2n) O(nlog2n)堆排序O(nlog2n) O(nlog2n) O(nlog2n)归并排序O(nlog2n...
  • 排序算法_复杂度总结

    2013-06-25 13:06:00
    冒泡排序算法时间复杂度是O(n^2) 选择排序算法时间复杂度是O(n^2) 插入排序算法时间复杂度是O(n^2) ...归并排序算法时间复杂度是O(nlogn) 相关概念 1、稳定排序(stable sort)和非稳定排序 稳
  • #归并排序的时间复杂度最坏情况和平均情况/期望运行时间为Θ(nlgn) 是典型的分治算法 import math #归并排序的主要函数 #数组A为待排序数组A[p...r],p&lt;=q&lt;r , 单独分析该函数时假设左数组和右数组...
  • 时间复杂度 n^2表示n平方,选择排序有时叫做直接选择排序或简单选择排序 排序方法 平均时间 最好时间 最坏时间 桶排序(不稳定) O(n) O(n) O(n) 基数排序(稳定) O(n) O(n) O(n) ...
  • 这是使用desmos画出来图形。...)低于xlog(x)高于x,但通常我们的排序算法快排和归并的最优是xlog(x)最坏是n^2, 通过二分查找改进的排序算法最坏是log(n!),最好是n。 虽然nlog(n)和log(n!)相差不...
  • 排序算法经过长时间演变,大体可以分为两类:内排序和外排序。在排序过程中,全部记录存放在内存,则成为内排序;如果排序过程中需要使用外存,则称为外排序,本文讲... 时间复杂度(最坏) 时间复杂度(最好) ...
  • 《忆排序 面试我最强》 作者:马士兵 选炮插, 快归堆希统计姬, n 方 n老 n一三, ...下列排序算法中,最坏时间复杂度是 O(n log(n)) 是? 快速排序 插入排序 归并排序(*) 堆排序(*) ...
  • 排序时间复杂度

    千次阅读 2013-08-07 00:49:54
    数组长度为N。 选择排序:将最小元素和第一个元素交换,第二小元素和第二个元素...时间复杂度达不到O(N^2),最坏情况为O(N^1.5) 归并排序:将两个有序数组合并为一个更大有序数组。时间复杂度为O(NlgN) 快速
  • 05时间复杂度时间频度①忽略常数项②忽略低次项③忽略系数一些说明常见的时间复杂度① 常数阶O(1)② 对数阶O(log2n)③ 线性阶O(n)④ 线性对数阶O(nlog2N)⑤ 平方阶O(n²)⑥ 立方阶O(n³)、K次方阶O(n^k)平均时间...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 697
精华内容 278
关键字:

归并排序的时间复杂度最坏