算法学习与代码实现3——合并排序

算法学习与代码实现3——合并排序

算法思路

合并排序使用的是分治法,分治法在《算法导论》上的介绍分为三个步骤:

  • 分解(Divide):将原问题分解成一些列子问题;
  • 解决(Conquer):递归地解各个子问题。若问题足够小,则直接求解。
  • 合并(Combine):将子问题的结果合并成原问题的解。

同时,书上也介绍了合并排序(merge sort)相对应的三个步骤:

  • 分解:将n个元素分成各含n/2个元素的子序列;
  • 解决:用合并排序法对两个子序列递归地;
  • 合并:合并两个已排序的子序列以得到排序结果。

其实可以说的通俗点,合并排序分为两个部分,核心部分就是合并,另一部分是合并排序。合并部分就是假设你手中有两摞已经排好序的扑克牌,牌面朝上,上面小下面大。然后每次比较两摞牌最上面的一张,取较小的一张面朝下放到新的一摞牌中。这样,在新的一摞牌中就实现了将两摞排好序的牌合成一摞。

另一部分是合并排序。作为递归的算法,合并排序必须对自己有信心——有信心能够完成排序,因为他要调用自己,对自己没有信心怎么调用自己呀。当然它的信心也是有依据的,它在内部实现中会将待排序的数分解成两份,对每份再次调用合并排序。当分解到足够小,也就是每份中只有一个数时,它自然就是排好序的了。这样,合并排序在内部就能够实现将待排序的数编程两份排好序的数,用刚才说到的合并处理一下,就编程一份排好序的数了。

算法性能

稳定性: 稳定排序

时间复杂度: O(nlogn)

空间复杂度: O(n)

伪代码

合并部分。MERGE过程的时间代价是O(n),在下面伪代码中,A是数组,p、q、r是下标,满足p≤q<r。该过程假设A[p..q]和A[q+1..r]都已排好序,并将它们合并成一个已排好序的子数组代替当前子数组A[p..r]。

MERGE(A, p, q, r)
n1 <- q - p + 1
n2 <- r - q
create arrays L[1..n1+1] and R[1..n2+1]
for i <- 1 to n1
    do L[i] <- A[p+i-1]
for j <- 1 to n2
    do R[j] <- A[q+j]
L[n1+1] <- ∞
R[n2+2] <- ∞
i <- 1
j <- 1
for k <- p to r
do if L[i] ≤ R[j]
    then A[k] <- L[i]
         i <- i + 1
else A[k] <- R[j]
    then A[k] <- R[j]
         j <- j + 1

下面是合并排序的伪代码,它对子数组A[p..r]进行排序

MERGE_SORT(A, p, r)
if p < r
    then q <- (p+r)/2向下取整
         MERGE-SORT(A, p, q)    ▷ 相信自己
         MERGE-SORT(A, q+1, r)
         MERGE(A, p, q, r)

C语言实现

merge函数实现如下:

void merge(int * array, int head, int middle, int tail){
    int n1 = middle - head + 1;
    int n2 = tail - middle;
    int * L = malloc(n1 * sizeof(int));
    int * R = malloc(n2 * sizeof(int));
    int i = 0, j = 0;
    for ( i = 0; i < n1; ++i) {
        L[i] = array[head + i];
    }

    for ( j = 0; j < n2; ++j) {
        R[j] = array[middle + 1 + j];
    }
    i = 0;
    j = 0;
    for ( int k = head; k < tail + 1; k++) {
        if ( i == n1 ) {
            array[k] = R[j++];
        }
        else if ( j == n2 ) {
            array[k] = L[i++];
        }
        else if ( L[i] <= R[j]) {
            array[k] = L[i++];
        }
        else if ( L[i] > R[j] ) {
            array[k] = R[j++];
        }
    }
    free(L);
    free(R);
}

有种说法叫过早优化是万恶之源,所以这里我没有对内存申请部分做优化,其实实现时可以做一点策略,当n1和n2小于一定值时使用固定分配内存,这样可以减少malloc过程的时间消耗。

merge_sort实现如下:

void merge_sort(int *array, int head, int tail){
    if ( head < tail ) {
        int middle = (head + tail) / 2;
        merge_sort(array, head, middle);
        merge_sort(array, middle + 1, tail);
        merge(array, head, middle, tail);
    }
}

本文代码托管于:
https://github.com/haoranzeus/algorithms_study

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值