分治法之归并排序

分治法之归并排序

目录

摘要

本小节中主要讨论分治法归并排序的应用:

  • 分治法的步骤
  • 主方法
  • 归并排序的算法
  • 归并排序的实例

分治法的步骤

  • Divide the problem into one or more subproblem
  • 分解:将原问题划分为一些子问题,子问题的形式和原问题的相同,但是规模减小
  • Conquer the subproblems by solving them recursively.
  • 解决:利用递归求解出子问题的解,如果子问题的规模足够小,则停止递归,直接求解
  • Combine the solutions to the subproblems into the solution for the original problem
  • 合并:将子问题的解合并成原问题的解
  • -

主方法(The master method)

Master theorem 是判断递归式,求解估算算法复杂度比较常用的方法,它的主要内容如下:

Master theorem (reprise)
T(n) = aT(n/b) + f(n)
CASE 1: f(n) = O(n log b a – ε ), constant ε > 0
⇒ T(n) = Θ(n log b a ) .
CASE 2: f(n) = Θ(n log b a lg k n), constant k ≥ 0
⇒ T(n) = Θ(n log b a lg k+1 n) .
CASE 3: f(n) = Ω(n log b a + ε ), constant ε > 0,
and regularity condition
⇒ T(n) = Θ(f(n)) .

主方法的实例

eg1 : T(n)=4T(n/2)+n

由于f(n)=n=O(n^2),属于CASE1,==>T(n)=Θ(n^2)


eg2 : T(n)=2T(n/2)+n

由于f(n)=n=Θ(n^2),属于CASE2,==>T(n)=Θ(nlgn)


eg3 : T(n)=4T(n/2)+n^3

由于f(n)=n^3=Ω(n^2),属于CASE3,==>T(n)=Θ(n^3)

归并排序

merge算法伪代码1 :

算法导论中的伪代码:使用哨兵

Merge算法伪代码2 :

Merge(B[0...p-1],C[0...q-1],A[0,p+q-1])
//将两个有序数组合并成一个有序数组
//输入:两个有序数组B[0...p-1],C[0...q-1]
//输出:A[0,p+q-1]中已经有序存放了BC的元素
 i<-0;j<-0;k<-0;
 while i<p && j<q do
    if B[i]<=C[j]
       A[k] <- B[i];i <- i+1
    else
       A[k] <- C[j];j <- j+1
    k <- k+1
 if i=p
    copyC[j...q-1] to A[k..p+q-1]
 else
    copyB[i...p-1] to A[k..p+q-1]

MergeSort算法伪代码1 :

这里写图片描述

MergeSort算法伪代码2 :

MergeSort(A[0,n-1])
//递归调用mergesort对数组A进行排序
//输入:一个可排序数组A[0...n-1]
//输出:非降序排列的数组A[0,n-1]
if n>1
  copy a[0...n/2] to B[0...n/2]
  copy a[n/2+1...n-1] to C[0...n/2]
MergeSort(B[0...n/2])
MergeSort(C[0...n/2])
Merge(B,C,A)

MergeSort 的例子 :

这里写图片描述

归并排序的实例1

eg : 应用归并算法将序列EXAMPLE按照字母表顺序排序

/*
* 归并排序:C 语言
*
* @author Erice_s
* @date 2017/10/13
*
* 参数说明:
*    a -- 一个可排序的数组
*    first -- 第一个有序数组下标最小的数
*    last -- 第二个有序数组下标最大的数
*    mid -- 第一个有序数组结束,第二个有序数组开始的下标
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void merge(char *a, int first, int mid, int last)
{ 
    int i = first;
    int j = mid + 1;
    int m = 0;

    char *ptmp = (char*)malloc((last - first + 1)*sizeof(char));
    while (i <= mid&&j <= last)
    {
        if ((int)a[i] < (int)a[j])
            ptmp[m++] = a[i++];
        else
            ptmp[m++] = a[j++];
    }
    while (i <= mid)
        ptmp[m++] = a[i++];
    while (j<=last)
        ptmp[m++] = a[j++];

    for (i = 0; i < last - first + 1; i++)
        a[first+i] = ptmp[i];

    free(ptmp);
}

void mergeSort(char *a, int first, int last)
{
    if (a==NULL||first >= last)
        return;
    int mid = (first & last) +((first^last) >> 1);
    //int mid = (first + last)/2;//may cause overflow
    mergeSort(a, first, mid);
    mergeSort(a, mid+1, last);
    merge(a,first,mid,last);
}
void print(char *p,int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%c", p[i]);
    }
    printf("\n");
}

int main(void)
{
    char p[] = { 'E','X','A','M','P','L','E' };
    int len = sizeof(p)/sizeof(*p);

    printf("mergeSort before: ");
    print(p, len);

    mergeSort(p, 0, len-1);

    printf("mergeSort  after: ");
    print(p, len);

    system("pause");

    return 0;
}

运行结果 :

归并排序结果


归并排序的实例2

eg 2 :有N个整数,A[1]…A[N],需要找到这样的(i,j)的数对d的数量;满足要求:

1 <= i < j<= N && A[i]>A[j]
数据范围:1<=N<=65537 , 0< A[i] <10^9
算法时间复杂度为O(nlgn)


思路分析:

采用归并排序的思想,先把问题的规模变小,问题可以简化为三个子问题,解决A[]左边存在多少逆序对,A[]右边存在多少逆序对,横跨mid的有多少逆序对。

逆序对问题


/*
* 归并排序:C 语言
*
* @author Erice_s
* @date 2017/10/13
*
* 参数说明:
*    a -- 一个可排序的数组
*    l -- 左边界
*    m -- 中值
*    r -- 右边界
*/

#define _CRT_SECURE_NO_WARNINGS
#include  <iostream>
#include  <cstdio>

using  namespace  std;

#define  MAXN  65537
int  a[MAXN];
int  la[MAXN], ra[MAXN];
long  long  cnt; // 逆序对数目

void  merge (int l,  int m, int r) //l  左边界,m 中值 , r 右边界
{
   int  p1 = 0, p2 = 0, j, k;
    for ( int i = l ; i <= m; i++)
        la[p1++] = a[i]; // 初始化 la 数组
    for (int  i = m+ 1; i <= r; i++)
        ra[p2++] = a[i]; // 初始化 ra 数组
        la[p1] = ra[p2] = 0x3f3f3f3f;
    j = k = 0;
    for (int  i = l; i <= r; i++)
    {
        if (la[j] <= ra[k])
            a[i] = la[j++];
        else
        {
            a[i] = ra[k++];
            cnt += p1 -j;
        }
    }
}

void  mergeSort( int l, int  r)
{
    if (l >= r)
            return;
    int  m = (l & r) +((l ^ r)>> 1);
    mergeSort(l, m);
    mergeSort(m + 1, r);
    merge(l, m, r);
}

int  main()
{
    int  n;
    while (~scanf("%d", &n))
    {
        cnt = 0;
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i]);
        mergeSort(0, n -1);
        printf("测试数组的元素个数: %d\n", n);
        printf("逆序对个数为:%lld \n", cnt);
    }
    return 0;
}

运行结果
逆序对

aha

好久没写过博客了,今天就写到这里了,欢迎小伙伴叨扰! merci…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Erice_s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值