归并排序求逆序数

1,707 阅读4分钟

归并排序

归并排序的思想

区间拆分

将要排序的区间向下递归地拆分成左右两个小区间,然后再向上合并左右即可。
显然,递归的尽头就是区间长度为1,换句话说就是区间[l,r]不满足l < r. ————————————————————————
所以,归并排序的主干代码就已经出来了:
检查区间是否可再分,即是否满足l < r
如果不可再分,就直接return(结束)。
如果可再分,就先依次对左、右区间进行排序,然后合并。
————————————————————————

void MergeSort(int l,int r)
{
    if(l<r)
    {
        int m = (l+r)/2;///不懂不要瞎改
        MergeSort(l,m);
        MergeSort(m+1,r);
        SubMergeSort(l,m,r);
    }
}

区间合并

下面按照排序结果为从小到大来讲

用两个指针分别指向要合并区间的起始位置,然后比较两个指针对应的数值。
谁小,就把对应的数值放到一个临时数组中,然后该指针向后移动一位。
直到其中一个指针走到了对应区间的尽头(即后面没有元素了)。
如果另一个区间还有元素,就把后面的元素也依次放入到临时数组中。
最后,再把临时数组中的元素拷贝到原数组中。

const int maxnum=100000;
int a[maxnum];      //进行排序的数组
int tem[maxnum];    //临时数组
/*合并两个闭区间[l,m]和[m+1,r]*/
void SubMergeSort(int l,int m,int r)
{
    int i = l;      //数组下标代替指针
    int j = m+1;    //数组下标代替指针
    int k = l;///注意这里是l不是1
    while(i<=m&&j<=r)
    {
        if(a[i]>a[j])
            tem[k++] = a[j++];
        else
            tem[k++] = a[i++];
    }
    while(i<=m) tem[k++] = a[i++];
    while(j<=r) tem[k++] = a[j++];
    /*从tem复制到a*/
    for(int i=l;i<=r;i++)   a[i] = tem[i];
}

利用归并排序求逆序数

什么是逆序数

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。
一个排列中逆序的总数就称为这个排列的逆序数。

如何求逆序数

根据上面逆序数的概念,可以这样理解:
一个序列的逆序数就是所有数字的逆序之和。
而每个数字的逆序就等于在这个数字前面并且比大于这个数字的元素个数

下面的排序是指从小到大的排序。

显然,在归并排序过程中,每次合并后的区间逆序数是0。
并且,合并前两个区间各自的逆序数也是0,因为它们的顺序已经排好了。
所以,我们只需要记录在区间合并过程中每个元素减少的逆序数就行了。
再具体一点:
左区间本身是有序的,所以左区间的所有元素在合并后的区间中的逆序数为0。
而右区间的元素在合并后的区间中的逆序数等于左区间中比该元素大的元素的个数。
这个个数就是从左区间当前指针开始到结束的元素的个数。[i,m].size() = m-i+1

求每个元素的逆序数

#include <iostream>
using namespace std;
const int maxnum=100000;

struct node{
    int val;
    int inv;
};

node a[maxnum];
node tem[maxnum];

/*合并两个闭区间[l,m]和[m+1,r]*/
void SubMergeSort(int l,int m,int r)
{
    int i = l;
    int j = m+1;
    int k = l;///注意这里是l不是1
    while(i<=m&&j<=r)
    {
        if(a[i].val>a[j].val)
        {
            a[j].inv += m-i+1;
            tem[k++] = a[j++];
        }
        else
            tem[k++] = a[i++];
    }
    while(i<=m) tem[k++] = a[i++];
    while(j<=r) tem[k++] = a[j++];
    /*从tem复制到a*/
    for(int i=l;i<=r;i++)   a[i] = tem[i];
}

void MergeSort(int l,int r)
{
    if(l<r)
    {
        int m = (l+r)/2;///不懂不要瞎改
        MergeSort(l,m);
        MergeSort(m+1,r);
        SubMergeSort(l,m,r);
    }
}


int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            cin>>a[i].val;
        }
        MergeSort(0,n-1);
        for(int i=0;i<n;i++)
        {
            cout<<a[i].val<<" ";
        }
        cout<<endl;
        for(int i=0;i<n;i++)
        {
            cout<<a[i].inv<<" ";
        }
        cout<<endl;
    }
    return 0;
}

求序列总的逆序数

#include <iostream>
using namespace std;
const int maxnum=100000;

int a[maxnum];
int tem[maxnum];

int ans;

/*合并两个闭区间[l,m]和[m+1,r]*/
void SubMergeSort(int l,int m,int r)
{
    int i = l;
    int j = m+1;
    int k = l;///注意这里是l不是1
    while(i<=m&&j<=r)
    {
        if(a[i]>a[j])
        {
            ans += m-i+1;///只计算总逆序数(相等的不算)
            tem[k++] = a[j++];
        }
        else
            tem[k++] = a[i++];
    }
    while(i<=m) tem[k++] = a[i++];
    while(j<=r) tem[k++] = a[j++];
    /*从tem复制到a*/
    for(int i=l;i<=r;i++)   a[i] = tem[i];
}

void MergeSort(int l,int r)
{
    if(l<r)
    {
        int m = (l+r)/2;///不懂不要瞎改
        MergeSort(l,m);
        MergeSort(m+1,r);
        SubMergeSort(l,m,r);
    }
}


int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        ans = 0;
        MergeSort(0,n-1);
        for(int i=0;i<n;i++)
        {
            cout<<a[i]<<"\t";
        }
        cout<<endl;
        cout<<ans<<endl;
    }
    return 0;
}