归并排序(Merge Sort)

"归并"的含义是将两个或两个以上的有序列表组合成一个新的有序列表。归并排序也称合并排序,注意这里指的归并排序默认指二路归并排序。

基本思想

假设初始序列含有n个元素,则可看成n个有序的子序列,每个子序列的长度是1。然后两两归并,得到 ⌈ n / 2 ⌉ \lceil n/2 \rceil n/2个长度为2或1的有序子序列;再两两归并,…,如此重复,直到得到一个长度为n的有序序列为止。(也即二路归并排序的实现)

伪代码实现

二路归并排序的核心操作是将列表中前后相邻的两个有序序列归并为一个有序序列。假设n个元素使用数组存储,二路归并排序的伪代码实现如下:

假设数组A[0...n-1]表示待排序数组
MergeSort(A[0...n-1])
    // 回归条件:传入数组的长度为1
    if(n<=1)
        return
    copy A[0...(n/2取下整)-1] to B[0...(n/2取下整)-1]
    copy A[(n/2取下整)...n-1] to C[0...(n/2取下整)-1]
    // 递推
    MergeSort(B[0...(n/2取下整)-1])
    MergeSort(C[0...(n/2取下整)-1])
    Merge(B,C,A)

数组B[0...p-1]和C[0...q-1]表示已排序数组
A[0...p+q-1]表示待排序数组
Mege(B[0...p-1],C[0...q-1],A[0...p+q-1])
while i<p-1 && j<q-1 then
    if(B[i]≤C[j]) then
        A[k] = B[i]
        i++
    else
        A[k] = C[j]
        j++
    k++
if(i == p) then
    copy C[j...q-1] to A[k...p+q-1]
else
    copy B[i...p-1] to A[k...p+q-1]

接下来考虑该算法的执行效率。根据伪代码实现,基于比较次数 C ( n ) C(n) C(n)的递推关系式如下:
当 n > 1 时 , C ( n ) = 2 C ( n / 2 ) + C m e r g e ( n ) 当n>1时,C(n) = 2C(n/2) + C_{merge}(n) n>1C(n)=2C(n/2)+Cmerge(n)
C ( 1 ) = n C(1) = n C(1)=n
这里 C m e r g e ( n ) C_{merge}(n) Cmerge(n)表示合并阶段进行比较的次数。显然,最坏的情况下, C m e r g e ( n ) = n − 1 C_{merge}(n)=n-1 Cmerge(n)=n1,所以其递推式为:
当 n > 1 时 , C w o r s t ( n ) = 2 C w o r s t ( n / 2 ) + n − 1 当n>1时,C_{worst}(n) = 2C_{worst}(n/2) + n-1 n>1Cworst(n)=2Cworst(n/2)+n1
根据分治法的效率求解,这里选择主方法求解。代入公式,可知a=2,b=2, f ( n ) = n − 1 f(n)=n-1 f(n)=n1,经计算 n l o g b a = n n{log_b^a} = n nlogba=n。因为 n − 1 n-1 n1 n n n多项式相等,所以根据主定理 T ( n ) = θ ( n l o g b a l o g k + 1 n ) = θ ( n l o g n ) T(n)= θ(n{log_b^alog^{k+1}n})=θ(nlogn) T(n)=θ(nlogbalogk+1n)=θ(nlogn)
这里给出java版本实现:

public class MergeSort {
    public void sort(int[] array) {
        if (array.length <= 1) {
            return;
        }
        int halfLength = array.length/2;
        halfLength += array.length % 2 == 0 ? 0 : 1;
        int[] leftHalfArray = new int[halfLength];
        copyArray(array, 0, leftHalfArray);
        int[] rightHalfArray = new int[array.length - halfLength];
        copyArray(array, halfLength, rightHalfArray);
        sort(leftHalfArray);
        sort(rightHalfArray);
        merge(leftHalfArray, rightHalfArray, array);
    }

    private void merge(int[] leftHalfArray, int[] rightHalfArray, int[] dstArray) {
        int i = 0, j = 0 ,k = 0;
        while (i < leftHalfArray.length && j < rightHalfArray.length) {
            if (leftHalfArray[i]<rightHalfArray[j]) {
                dstArray[k] =leftHalfArray[i++];
            } else {
                dstArray[k] = rightHalfArray[j++];
            }
            k++;
        }
        if(i == leftHalfArray.length) {
            while (j<rightHalfArray.length) {
                dstArray[k++] = rightHalfArray[j++];
            }
        } else {
            while (i<leftHalfArray.length) {
                dstArray[k++] = leftHalfArray[i++];
            }
        }
    }

    private void copyArray(int[] srcArray, int beginSubscript, int[] dstArray) {
        for(int i = 0; i < dstArray.length; i++) {
            dstArray[i] = srcArray[i + beginSubscript];
        }
    }
}

更多归并排序的排序实现可以参考力扣
需要说明的是,尽管归并排序的算法时间复杂度是 n l o g ( n ) nlog(n) nlog(n)(与快速排序时间复杂度相当),但是因为该算法需要线性的额外空间,且算法过于复杂,从而只具有理论上的意义。

参考

《算法设计与分析基础》 第三版 Anany Levitin 著 潘彦 译
《数据结构 严蔚敏 吴伟民 著
https://leetcode-cn.com/problems/sort-an-array/ 排序数组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值