归并排序之求逆序对

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41550842/article/details/81215935

何谓归并排序,先看下面一个例子:

设有数列{6,202,100,301,38,8,1}

初始状态:6,202,100,301,38,8,1

第一次归并后:{6,202},{100,301},{8,38},{1};

第二次归并后:{6,100,202,301},{1,8,38};

第三次归并后:{1,6,8,38,100,202,301};

最终结果得出{1,6,8,38,100,202,301}

归并排序是一种稳定且高效的排序算法,时间复杂度为O(nlog2n) ,一般按照三步走:

1.划分问题:把序列分成元素个数尽量相等的两半

2.递归求解:把两半元素分别排序

3.合并问题:把两个有序表合并为一个

前两步很容易实现,关键在于第三步,怎么合并呢? 很简单,每次只需把左右两边序列的最小元素进行比较,把其中较小的元素加入到合并后的辅助数组中即可。示例代码如下:

//归并排序
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N] ,b[N];//b为辅助数组
void merge_sort(int l , int r)
{
    if(r-l > 0)//如果整个区间中元素个数大于1,则继续分割
    {
        int mid = (l+r) / 2 ;
        int i = l; //辅助数组的下标
        int p = l , q = mid+1;
        merge_sort(l , mid);
        merge_sort(mid+1 , r);
        //printf("%d-%d  %d-%d\n",p,mid ,q ,r);
        while(p<=mid || q<=r)//左右两部分只要有一部分不为空
        {
            if(q>r || (p<=mid && a[p]<=a[q]))//从左半数组复制到辅助数组
                b[i++] = a[p++];
            else b[i++] = a[q++];//从右半数组复制到辅助数组
        }
        for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中
            a[i] = b[i];
    }
}
int main()
{
    int n;
    while(cin >> n)
    {
        for(int i = 1 ; i <= n; i ++)
            cin >> a[i];
        merge_sort(1 , n);
        for(int i = 1; i <= n; i++)
            cout << a[i] << " ";
        cout << endl;
    }
    return 0;
}

求序列的逆序对,先看下面的例子:

设有数列{6,202,100,301,38,8,1}

初始状态:6,202,100,301,38,8,1

第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;

第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;

第三次归并后:{1,6,8,38,100,202,301},比较次数:4;

总的比较次数为:3+4+4=11;

逆序数为14;

根据归并排序的特性(左右两部分的有序序列合并时,假设i在左边,j在右边,对于右边的j,统计左边比它大的元素个数f(j),则f(j) = mid-i+1 ,合并万所有的序列时即可得出答案,即f(j)之和便是答案),只需将上面的代码修改一处:把“else  b[i++] = a[q++];”改成“ else {b[i++] = a[q++];  cnt += mid-p+1;}" ,注意在调用之前将cnt清零。

//归并排序及求逆序对
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N] ,b[N];//b为辅助数组
long long cnt;
void merge_sort(int l , int r)
{
    if(r-l > 0)//如果整个区间中元素个数大于1,则继续分割
    {
        int mid = (l+r) / 2 ;
        int i = l; //辅助数组的下标
        int p = l , q = mid+1;
        merge_sort(l , mid);
        merge_sort(mid+1 , r);
        //printf("%d-%d  %d-%d\n",p,mid ,q ,r);
        while(p<=mid || q<=r)//左右两部分只要有一部分不为空
        {
            if(q>r || (p<=mid && a[p]<=a[q]))//从左半数组复制到辅助数组
                b[i++] = a[p++];
            else
            {
                b[i++] = a[q++];
                cnt += mid -p +1;//将逆序对的个数累加起来
            }
        }
        for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中
            a[i] = b[i];
    }
}
int main()
{
    int n;
    while(cin >> n)
    {
        for(int i = 1 ; i <= n; i ++)
            cin >> a[i];
        cnt = 0;
        merge_sort(1 , n);
        for(int i = 1; i <= n; i++)
            cout << a[i] << " ";
        cout << endl;
        cout << "逆序对有:" << cnt <<endl;
    }
    return 0;
}

当然求逆序对还可以用树状数组求,可以百度一波

猜你喜欢

转载自blog.csdn.net/qq_41550842/article/details/81215935
今日推荐