merge_sort——逆序对的数量(归并排序)

思路:
1.暴力做法:两重循环一个个例举,第一层循环,i-> n ,第二层循环,j=i+1->n ,例如先例举i=2,j=3,4,5,6,1.,然后例举i=3,j=4,5,6,1。但是一般情况下会超时
2 归并排序:递归回溯,从小到大地合并,两个子序列,左边(l,mid),右边(mid+1,r),两个序列比较,当左边到进行到i时,q[i]>q[j],那么后面的mid-i个数都比去此时的q[j]大,即有mid-i+1个逆序对(i左边的序列,j是右边的序列)。用一个数记录个数,注意题目数据范围可能会爆int,所以用long long

如果您对归并排序还不了解,请您移步这篇博客 https://www.cnblogs.com/expect-999/p/17599008.html

如下图举例

从下到上回溯 (先左边回溯完,再右边回溯)
左逆序对 | (9,1)| (7,6)|,|(9,6)|(9,7) 数量 1+1+1+1
右逆序对 | (6,3) |(3,2),(6,2) | 数量 1+2
最终左右合并 逆序对 |(6,2),(7,2),(9,2)|(6,3),(7,3),(9,3)|(7,6),(9,6)|(9,8)| 数量 3+3+2+1

所以统计次数就每次 cnt+=mid-i+1;,即cnt=1+1+1+1+1+2+3+3+2+1=16;
运行实例结果如下图

先回溯左边,再回溯右边

C++代码如下

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e5+10;

int n;
int q[N],p[N];

long long ans;

void merge_sort(int q[],int l,int r)
{
    if(l>=r) return;
    int mid = l + r >> 1;
    
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    
    int i=l,j=mid+1,cnt=0;
    // int tmp=ans;
    while(i<=mid && j <=r)
    {
        if(q[i]<=q[j]) p[cnt++]=q[i++];
        else
        {
             p[cnt++]=q[j++];
             ans+=mid-i+1;
            //  printf("%d ",mid-i+1);
        }
    }
    // if(ans>tmp) puts("");
    while(i<=mid) p[cnt++]=q[i++];
    while(j<=r) p[cnt++]=q[j++];
    
    for(int i=l,cnt=0;i<=r;) q[i++]=p[cnt++];
}

signed main()
{
    cin >> n;
    
    for(int i=0;i<n;++i) cin >> q[i];
    
    merge_sort(q,0,n-1);
    
    cout << ans <<endl;
    
    return 0;
}

本人蒟蒻,如有错误或不恰当的地方还望指点,如果对您有帮助,希望给个免费的点赞,这对我很重要,感谢观看我的博客

posted @ 2024-04-18 16:59  小卷同学  阅读(38)  评论(0编辑  收藏  举报