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

来源:互联网 发布:java注释有几种 编辑:程序博客网 时间:2024/06/06 00:39

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

分治法的一个应用就是归并排序,是一种稳定的快速的排序,(Θ(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++;ElseA[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 0
原创粉丝点击