归并排序&逆序数的nlogn算法

首先说一下归并排序, 时间复杂度nlogn, 将一段数, 分成小段排序, 再合并排序;

void Mergesort(vector<int> &a, int left, int right, int mid){
    vector<int> b;
    int i, j, k;
    i=left, j=mid+1, k=0;
    while(i<=mid&&j<=right){
        if(a[i]<=a[j]) b.push_back(a[i++]);
        else b.push_back(a[j++]);
    }
    while(i<=mid) b.push_back(a[i++]);
    while(j<=right) b.push_back(a[j++]);
    for(i=left, j=0; i<=right; i++, j++){
        a[i]=b[j];
    }
}
void Merge(vector<int> &a, int left, int right){
    if(left<right){
        int mid=(left+right)/2;
        Merge(a, left, mid);
        Merge(a, mid+1, right);
        Mergesort(a, left, right, mid);
    }
    return;
}

再看看逆序数:(以下来自百度百科)

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。一个排列中所有逆序总数叫做这个排列的逆序数。也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。

那么逆序数有什么应用呢?

现在给出一个数列, 求该数列至少交换几次数字(只可以交换相邻的数字)可以变成顺序数列?则数列的逆序数个数即为交换次数;

如数列: 6, 5, 3, 7, 8, 1中逆序数对有8个;那么他是如何交换的呢?

  1. 8,1交换 ->6,5,3,7,1,8;
  2. 7,1交换 ->6,5,3,1,7,8;
  3. 3,1交换 ->6,5,1,3,7,8;
  4. 5,1交换 ->6,1,5,3,7,8;
  5. 6,1交换 ->1,6,5,3,7,8;
  6. 3,5交换 ->1,6,3,5,7,8;
  7. 3,6交换 ->1,3,6,5,7,8;
  8. 5,6交换 ->1,3,5,6,7,8;

这已是最简单变换;因为 已知数列中需要交换的是前后两个数位置顺序相反的,反之无需交换, 此时交换次数最少;

若数列中a, b位置顺序相同, 交换a, b 后必定要再次将a, b交换回来, 此时必将多交换两次;

那么怎么求数列中的逆序数呢?

最简单的方法就是从头到尾, 一个数一个数的比较, O(n^2)的复杂度;

int inversion(vector<int> a){
    int cnt=0;
    int n=a.size();
    for(int i=0; i<n; i++)
        for(int j=0; j<i; j++)
            if(a[i]<a[j]) cnt++;
    return cnt;
}

还有时间复杂度更小的吗,比如O(nlogn)?

这个还真有,这就利用了归并排序的思想,分而治之;

我们在归并排序时,是将数列分成小段, 然后排序,排序前数列的总逆序数与排序后的相比差别就是少了经过排序的每小段中的逆序数;还是用上边的数列为例:6,5,3,7,8,1;

分成两段(6,5,3为一段,7,8,1为一段)排序后:3,5,6,1,7,8;此时逆序数为3;排序前第一段逆序数:3, 第二段逆序数:2, 合计为5;排序前数列总序列为3+8=5;

int Mergesort(vector<int> &a, int left, int right, int mid){
    vector<int> b;
    int i, j, k, cnt;
    i=left, j=mid+1, k=0, cnt=0;
    while(i<=mid&&j<=right){
        if(a[i]<=a[j]) b.push_back(a[i++]);
        else b.push_back(a[j++]), cnt+=mid-i+1;//i~mid和mid+1~j已经是顺序的了, 所以如果a[i]>a[j]则i~mid都>a[j], 此时一共mid-i+1个逆序数
    }
    while(i<=mid) b.push_back(a[i++]);
    while(j<=right) b.push_back(a[j++]);
    for(i=left, j=0; i<=right; i++, j++){
        a[i]=b[j];
    }
    return cnt;
}
int cnt;
int Merge(vector<int> &a, int left, int right){
    if(left<right){
        int mid=(left+right)/2;
        Merge(a, left, mid);
        Merge(a, mid+1, right);
        cnt+=Mergesort(a, left, right, mid);
    }
    return cnt;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值