归并排序和快排会遇到的问题,

1. 我们知道它们都使用了分治算法:将原问题分割成同等结构的子问题,子问题解决后,原问题也得到了解决。

衍生出来的问题:

1)逆序对:

 对应题目:

剑指:数组中的逆序对,在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。

如下图:23是正序对,而21是逆序对

解法一: 暴力解法,考察每一个数对。算法复杂度:O(n^2)

 解法二:归并排序的思路来求逆序对的个数。算法复杂度:O(nlogn)

假设正在归并上面的数组,左侧的2,3,6,8和右侧的1,4,5,7已经排好序了,左侧和右侧内部都没有逆序对,而从左侧取一个数,从右侧取一个数,则有可能形成逆序对。

例如,开始左侧拿出2,右侧拿出1,可知2>1,形成了逆序对。此时逆序对只是加1吗?并不是,因为2右边的数都是大于2的,所以可以判断左边的数和右边的1可以形成4对逆序对((2,1)、(3,1)、(6,1)、(8,1))。

接下来比24,不会形成逆序对。再比34,不会形成逆序对。

当比较到64的时候,形成了逆序对,个数为2((6,4)、(8,4))。

 

 

归纳一下,也就是在归并的时候,如果右侧的元素小于左侧的元素,这个时候开始统计逆序对就行了,如果左侧的索引为i,左侧的末尾元素的索引为mid,逆序对个数就为mid-i+1

这样并没有结束,前面的假设是左侧和右侧是有序的,事实上并不是,左侧和右侧也进行了归并的过程才能变得有序,而在归并过程中,也能计算出逆序对的个数。

所以:

总的逆序对的个数=左侧归并时求得的逆序对个数 + 右侧归并时求得的逆序对个数 + 对整体进行归并时的逆序对个数。

注意:这三种情况是没有重复的。左侧归并找到的逆序对相当于从左侧数组中取2个数,而整体归并的时候是分别从左右数组中取1个数,所以不可能发生重复!

 

#define div 1000000007
class Solution {
public:
          
    int InversePairs(vector<int> data) {
        int n = data.size();
        vector<int> aux(data);   //辅助空间
        return mergeSort(data, aux, 0, n-1);
    }
    
    int mergeSort(vector<int> &arr, vector<int> &aux, int l, int r){
        if(l>=r) return 0;
        int mid = (l+r)/2;
        int left = mergeSort(arr, aux, l , mid)% div ;  //统计左部分逆序数的个数
        int right = mergeSort(arr, aux, mid+1, r)% div; //统计右部分逆序数的个数
        return (left + right + merge(arr, aux, l , mid, r)) % div;  //左+右+全体
    }
    
    int merge(vector<int> &arr, vector<int> &aux, int l, int mid, int r){
        for( int i = l ; i <= r; i ++ )
            aux[i] = arr[i];
        int res = 0;  //统计逆序对的数量
        //初始化,i指向左半部分的起始索引位置l,j指向右半部分起始索引位置mid+1
        int i = l, j = mid+1;
        for(int k = l; k<=r; k++){
            if(i>mid){
                //左部分已经遍历完,还剩下右部分
                arr[k] = aux[j];
                j++;
            }
            else if(j>r){
                arr[k] = aux[i];
                i++;   
            }
            else if(aux[i]<aux[j]){
                arr[k] = aux[i];
                i++;
            }
            else{ //当左部分的aux[i]>aux[j],此时下标从i到mid与此时的aux[j]都组成了逆序对
                arr[k] = aux[j];
                j++;
                res += (mid - i +1);
                res %= div;
            }
        }
        return res;
    }
};

 

转载于:https://www.cnblogs.com/Bella2017/p/10136411.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值