分治法 归并排序 加上逆序数求法

分治法:
字面意义就是分而治之,把大的问题分成小的相似的子问题,递归的解决掉小问题,最终合起来解决大问题
分治法在每层递归时都有三个步骤:
分解原问题为若干子问题,这些子问题都是原问题的规模较小的实例;
解决这些子问题(递归)
合并这些子问题的解

分治法的一个应用就是归并排序,是一种稳定的快速的排序,(Θ(n*logn))
分解: 待排序的n为 n/2 n/2两组
解决: 归并排序递归的解决两组
合并: 合并两个已经有序的子序列
关键步骤为合并步(Merge),需要Θ(n)时间
伪代码如下:

Merge(A,p,q,r)

 N1 = q-p+1;
N2 = r-q;
New arrays L[N1 + 2], R[N2+2];
For(i= 1; i<=N1;i++ )
L[i] = A[p+i-1];
For(j = 1;j <=N2;j++)
R[j] = A[q+j];

L[N1+1] = 无穷大;
R[N2+1] = 无穷大;//哨兵牌 避免每次检查是否有空
I = 1; j= 1;
For(k = p; k<=r;k++)
If(L[i] < R[j])
A[k] = L[i];i++;
Else
A[k] = R[j];j++;
MergeSort(A,p,r):
If(p<r)
{
    Q = (p+r)/2;
mergeSort(A,p,q);
mergeSort(A,q+1,r);
merge(A,p,q,r);
}

C++代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
const int mm = (1<<30);
int count = 0;//用来求逆序数
void mergee(int A[], int p, int q, int r)
{
    int n0 = r-p+1, n1 = q-p+1,n2 = r-q;
    int L[(n0+1)/2], R[(n0+1)/2];
    int i,j;
    for(i=1;i<=n1;i++)
        L[i] = A[p+i-1];
    for(j = 1;j<=n2;j++)
        R[j] = A[q+j];
    L[n1+1] = mm; R[n2+1] = mm;
    i = 1; j = 1; int k;
    for(k = p; k<=r;k++)
    {
        if(L[i]<=R[j])//加上等号比较保险
        {
            A[k] = L[i]; i++;
        }
        else
        {
            A[k]= R[j];j++;
            count += n1-i+1;//后面解释
        }
    }
}
void merge_sort(int A[], int p, int r)
{
    if(p<r)
    {
        int q = (p+r)/2;
        merge_sort(A,p,q);
        merge_sort(A,q+1,r);
        mergee(A,p,q,r);
    }
}
int main()
{
    int n;
    while(cin>>n)
    {
        int num[n+2];
        for(int i = 1; i<=n; i++)
            cin>>num[i];

        merge_sort(num,1,n);

        for(int i = 1; i<=n; i++)
            cout<<num[i]<<" ";
        cout<< count<<endl;
    }
}

归并排序算法分析:
分解:计算中间位置 Θ(1);
解决: 2* T(n/2);
合并:Θ(n);
T(n) = Θ(1)(n == 1)
T(n) = Θ(n) + 2*T(n/2) (n>1);

关于逆序数的补充
如果排在前面的数比排在后面的数大,那就构成了一对逆序,比如
1 6 8 2 7
逆序有 6-2 8-2 8-7
用分治法的思想呢, 当两组数分别是(1,6,8) (2,7)时,进行归并排序时
先拿出左边的1, 再拿出右边的2放进来时,实际要放在6的前面,因为2比较小,因此对2来讲,就有逆序6和8两个,也即是左边组的长度从减掉前面的 用上面的代码表示就是 n1-i+1注意是n1而不是中间的q,而再选左边的6时,不会有位置的变化,不会产生逆序

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值