归并排序以及逆序对统计

3 篇文章 0 订阅
2 篇文章 0 订阅

归并排序以及逆序对统计

1. 归并排序

归并排序利用分治的方法,将两个有序数组进行合并,达到排目的。有序数组可以通过不停地将数组进行二分,最终得到一个数,认为此数组有序。然后将两个一个数的数组进行合并,得到一个有序的有两个数据的数组,然后返回上一层继续合并,最终得到有序数列。

第一步:解决两个有序数组合并

void mergeArray(int a[],int m, int b[], int n, int re[])    //a,b,合并到re;m,n分别为a,b数组的长度
{
    int i,j,k;      //给两个数组分别设定一个指针,把两个之中较小的放在re中,本次较小的指针后移
    i=j=k=0;
    while(i<m && j<n)                                   //合并相同长度的数据
    {
        if(a[i]>b[j])
            re[k++]=b[j++];
        else
            re[k++]=a[i++];
    }
    while(i<m)                                          //多出的部分直接复制在re中
        re[k++]=a[i++];
    while(j<n)                                          //多出的部分直接复制在re中
        re[k++]=b[j++];
}

第二步:归并排序

void mergeArray(int a[],int left,int mid,int right, int tmp[])   
//合并a[left...mid],a[mid+1...right]
{
    int i=left,j=mid+1,k=0;
    int m=mid,n=right;
    while(i<=m && j<=n)
    {
        if(a[i]>a[j])
            tmp[k++]=a[j++];
        else
            tmp[k++]=a[i++];
    }
    while(i<=m)
        tmp[k++]=a[i++];
    while(j<=n)
        tmp[k++]=a[j++];
    for(i=0;i<k;i++)    
 //本层合并完两个有序数组,返回到上一层位置,作为上一层的有序数组的基础,进而进行上一组的合并操作
        a[left+i]=tmp[i];
}
void mergeMain(int a[],int left, int right, int temp[])
{
    int mid;
    if(left<right)
    {
        mid=left+(right-left)/2;
        mergeMain(a,left,mid,temp);             //左边递归二分数组
        mergeMain(a,mid+1,right,temp);          //右边递归二分数组
        mergeArray(a,left,mid,right,temp);      //合并操作
    }
}
bool mergeSort(int a[],int n)
{
    int *re;
    re=new int[n];                             //一次性分配好空间,不能每次分配,释放
    if(!re)                                    //检查空间是否分配成功
        return false;
    mergeMain(a,0,n-1,re);
    delete[] re;
    re=NULL;                                    //空指针
    return true;
}

归并排序精简版

void merge_sort(int A[],int l,int r,int T[])
{
    if(l<r)
    {
        int m=l+(r-l)/2;        //由于通过折半划分数组,因此数组长度要么相等,要么规定左边比右边大,并且只大1
        merge_sort(A,l,m,T);
        merge_sort(A,m+1,r,T);
        int p=l,q=m+1,i=l;
        while(p<=m || q<=r)     //保证两个数组都扫描完
        {
            if(q>r || (p<=m && A[p]<=A[q])) T[i++]=A[p++];  
        //利用||运算的“短路运算”,当q>r时(表达式直接为真,不执行||运算后面语句),说明第二个数组已经没有元素了,直接复制第一个数组就可以了
            else T[i++]=A[q++];
        }
        for(i=l; i<=r; i++) A[i]=T[i];
    }
}

2. 逆序对

逆序对数量查找可以通过归并排序的交换次数算出,在数组a[left...mid]和a[mid+1..right],left<= i <=mid, mid+1<= j< =right ,当满足a[i] > a[j]时存在逆序对,假设a[j]放在a[i]之前,则a[i]到a[mid](因为a数组为有序数组)的值都大于a[j],共有mid-i+1个逆序对,因此只需要在归并排序中合并数组的时候稍加改动即可

int ans=0;                                                       //全局变量统计数目
void mergeArray(int a[],int left,int mid,int right, int tmp[])  
{
    int i=left,j=mid+1,k=0;
    int m=mid,n=right;
    while(i<=m && j<=n)
    {
        if(a[i]>a[j])
        {
            tmp[k++]=a[j++];
//当a[i]>a[j]的时候,如果此时把a[j]放在a[i]前面,那么a[i]到a[mid]的值都大于a[j],因此在交换之前属于逆序对
//左边数组 9 8 7  右边数组 1 2 3
//当把 1 放在 9 前面,那么9 8 7 都是 1 的逆序对,数量 mid-1+1
            ans+=mid-i+1;
        }
        else
            tmp[k++]=a[i++];
    }
    while(i<=m)
        tmp[k++]=a[i++];
    while(j<=n)
        tmp[k++]=a[j++];
    for(i=0;i<k;i++)  
        a[left+i]=tmp[i];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值