归并排序【MergeSort】

标签: MergeSort 归并排序
2人阅读 评论(0) 收藏 举报
分类:

归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案”修补”在一起,即分而治之)。

首先我们可以思考:如何将两个有序的序列合并,并且合并后依然有序呢?
这个比较简单,我们只需要用两个指针,一开始都指向两个序列的头部,然后开始比较两个指针所指数的大小,将小的那个放入一个新建的空间中,然后将它从相应的队列中删除,同时相应队列的指针后移一位;如此往复这个操作,那么两个有序的序列就合并了。

public int[] merge(int arrA[], int arrB[]) {
    // 创建合并后的数组
    int[] result = new int[arrA.length + arrB.length];
    int p1 = 0; // arrA的指针
    int p2 = 0; // arrB的指针
    int i = 0;  // 当前要放入result的位置
    // 比较两个指针所指数的大小,将小的那个放入新的数组中,同时相应队列的指针后移
    while(p1 < arrA.length && p2 < arrB.length) {
        result[i++] = arrA[p1] < arrB[p2] ? arrA[p1++] : arrB[p2++];
    }

    // 现在必有一个序列已经过完了,下面的循环只会进入一个
    while(p1 < arrA.length) {
        // 当前arrB走完了,将arrA中剩下的直接放入结果数组中
        result[i++] = arrA[p1++];
    }

    while(p2 < arrB.length) {
        // 当前arrA走完了,将arrB中剩下的直接放入结果数组中
        result[i++] = arrB[p2++];
    }
    return result;
}

归并排序就用到了这样的思想:先把大的数组拆分成两个序列,将这两个序列分别进行排序,然后再合并起来。
可能有人会想,拆分成的两个序列又怎么排序呢?很简单,我们继续将它拆分,当其不能拆分了,就认为这个不能拆分的个体已经排好序了,然后就可以进行合并了。

假设初始的数组是[5,6,4,7,3,2],我们可以先将其分成两个小的部分进行排序,进而再合并起来。
1. 首先拆分成两个部分:[5,6,4][7,3,2],将这两个部分分别排序后,再用外排的方式将两者合并排序起来。但是这两个部分怎么分别排序呢?我们可以继续拆分
2. 将[5,6,4][7,3,2]两个部分再进行拆分,得到:[5,6],[4][7,3],[2];现在又可以继续将它们拆分
3. 继续拆分得到{[5],[6][4]} {[7][3],[2]}; 现在已经无法继续拆分了,就可以进行合并了
4. 第一次归并后:[5,6][4][3,7][2]
5. 第二次归并后:[4,5,6][2,3,7]
6. 第三次归并后:[2,3,4,5,6,7]

算法实现

public class MergeSort {

    public void mergeSort(int[] arr) {
        if(arr == null || arr.length < 2) {
            return ;
        }
        mergeSort(arr, 0, arr.length - 1);
    }

    /**
        * 将数组在指定范围内进行拆分合并
        */
    public void mergeSort(int[] arr, int l, int r) {
        // 已经无法再拆分了
        if(l == r) {
            return ;
        }
        // 从中间拆分
        int mid = l + ((r-l) >> 1);
        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);
        // 进行合并
        merge(arr, l, mid, r);
    }

    /**
    * 合并,l到m,m+1到r 两段分别有序
    */
    public void merge(int[] arr, int l, int m, int r) {
        int[] help = new int[r - l + 1];
        int i = 0;
        int p1 = l;
        int p2 = m + 1;
        while (p1 <= m && p2 <= r) {
            help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
        }
        while (p1 <= m) {
            help[i++] = arr[p1++];
        }
        while (p2 <= r) {
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
            arr[l + i] = help[i];
        }
    }
}

时间复杂度

这里到了递归的算法,每一个递归都可以转换成非递归的方式。
递归分治法有一个Master公式,可以用来求解时间复杂度
简单的提一下Master公式:
T(N) = a * T(N/b) + O(N^d), N表示样本量,N/b表示子过程的样本量,a表示子过程执行次数,O(N^d)表示除去子过程之外,剩下的时间复杂度

我们这里是将数组从中间进行拆分,分成两段分别排序,所以子过程样本量是N/2;左右两个子过程,所以执行两次,a为2;剩下了一个合并的排序,时间复杂度为O(N)
所以最终的Master公式是:T(n)=2T(n/2)+O(N),

得到公式后,就可以去套答案了:
1. log(b,a) > d: 时间复杂度O(N^log(b,a))
2. log(b,a) = d: 时间复杂度O(N^d * logN)
3. log(b,a) < d: 时间复杂度O(N^d)

所以最终的时间复杂度是:O(N*logN)

空间复杂度

O(N)

稳定性

归并排序是稳定的排序

查看评论

数据结构JAVA版2017教学视频课程

-
  • 1970年01月01日 08:00

【从零学习经典算法系列】分治策略实例——归并排序(Mergesort)

归并排序是递归算法的一个很好的实例,该算法的基本操作是合并两个已排序的表,因为这两个表是已排序的,所以若将输出放到第三个表中时,该算法可以通过对输入数据一趟排序来完成。 算法流程:基本的合并算法是取两...
  • JasonDing1354
  • JasonDing1354
  • 2014-07-13 17:20:37
  • 1733

链表和归并排序(Merge Sort)

归并排序适合于对链表进行原址排序,即只改变指针的连接方式,不交换链表结点的内容。 归并排序的基本思想是分治法:先把一个链表分割成只有一个节点的链表,然后按照一定顺序、自底向上合并相邻的两个链表。 ...
  • Dread_naught
  • Dread_naught
  • 2014-12-12 11:01:27
  • 1127

面试之路(16)-归并排序详解(MergeSort)递归和非递归实现

归并排序的概念及定义归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。归并排序是建立在归...
  • u010321471
  • u010321471
  • 2016-05-01 16:58:42
  • 3649

C++实现归并排序Mergesort(使用递归的方法)

归并排序的最坏运行时间为O(NlogN)O(N\log N)。它的思想是把两个有序的子序列,通过一次遍历,合并且有序的放在第三个序列中。显然合并两个已排序的表的时间是线性的,最多进行N−1N-1次比较...
  • cjbct
  • cjbct
  • 2017-01-10 11:57:42
  • 2597

无聊写排序之 ---- 归并排序(MergeSort) 非递归实现

前面讲了归并排序的递归实现,虽然递归实现代码简洁容易理解,但是在实际执行中由于...
  • dreamhougf
  • dreamhougf
  • 2014-10-29 11:09:40
  • 1021

排序算法详解【归并排序-Merge_Sort】

归并排序详解
  • linsheng9731
  • linsheng9731
  • 2014-04-04 13:42:43
  • 9501

归并排序(MergeSort)两种实现方式比较

前面我写的归并排序实现,虽然原理上没什么问题,但算法实现不是很理想,今天没什么事情,重新优化了一下,这里比较一下: 1) 第1种方式,我采用了辅助存储来进行归并,代码如下: private voi...
  • hawksoft
  • hawksoft
  • 2011-09-11 20:47:37
  • 2041

C语言-数据结构-归并排序(merge sort)-递归 迭代-源代码及分析

1. 归并排序 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全...
  • kuweicai
  • kuweicai
  • 2017-01-25 21:30:06
  • 438

Java排序算法(三)--归并排序(MergeSort)递归与非递归的实现

归并排序的思想是: 1.将原数组首先进行两个元素为一组的排序,然后合并为四个一组,八个一组,直至合并整个数组; 2.合并两个子数组的时候,需要借助一个临时数组,用来存放当前的归并后...
  • y999666
  • y999666
  • 2016-03-21 09:39:22
  • 3740
    个人资料
    持之以恒
    等级:
    访问量: 2万+
    积分: 1091
    排名: 4万+
    最新评论