JZoffer51-数组中的逆序对(归并排序解决)

leetcode_jzoffer_51

分析:

1.arr[j]<arr[i],则arr[j]<从i开始到mid的所有左区间元素,构成了逆序对,则有mid-i+1个逆序对

2.在归并排序调序的过程中(移动i,j的过程中)顺便统计了逆序对。逆序对只可能出现在merge过程,右区间元素<左区间元素时才构成逆序对。

3.merge操作:合并数组的两个有序区间为新的有序区间,返回合并过程中顺便统计到的逆序对个数。

代码实现:

public class JZ51_ArrayReversePair {
    public int InversePairs(int [] array) {
        return reversePairsHelper(array,0,array.length-1);
    }
    /**
     * 传入一个数组nums,就可以求出nus[l...r]上的逆序对个数
     * @param nums
     * @param left
     * @param right
     * @return 此时nums[l...r]逆序对个数
     */
    private int reversePairsHelper(int[] nums, int left, int right) {
        if(left>=right){
            //区间只剩一个元素了,不构成逆序对
            return 0;
        }
        int mid=left+((right-left)>>1);
        //先递归求出左区间逆序对
        int leftPairs=reversePairsHelper(nums,left,mid);
        //再求出右区间逆序个数
        int rightPairs=reversePairsHelper(nums,mid+1,right);
        //当左右区间没有排序好,进入merge求其中的逆序对个数,否则左右已排序好merge中就没有逆序对,直接返回leftPairs+rightPairs
        if(nums[mid]>nums[mid+1]){
            //左区间最后的元素>右区间起始元素,未排序好
            return merge(nums,left,mid,right)+leftPairs+rightPairs;
        }
        //此时左右区间有序,整个集合也有序
        return leftPairs+rightPairs;
    }

    /**
     * 合并nums的两个有序区间[l...mid][mid+1...r]
     * @param nums
     * @param left
     * @param mid
     * @param right
     * @return 合并过程中逆序对的个数
     */
    private int merge(int[] nums, int left, int mid, int right) {
        int[] aux=new int[right-left+1];
        //合并过程中产生的逆序对个数
        int res=0;
        for (int i = 0; i < aux.length; i++) {
            aux[i]=nums[i+left];//nums有left个单位的偏移量
        }
        //左右两个小数组开始位置的索引
        int i=left;
        int j=mid+1;
        //k表示当前正在合并的原数组的索引下标
        for (int k = left; k <=right; k++) {
            if(i>mid){//左侧合并完了,放入右区间元素
                nums[k]=aux[j-left];
                j++;
            }else if(j>right){//右侧小数组遍历完了,左侧放入
                nums[k]=aux[i-left];
                i++;
            }else if(aux[i-left]<=aux[j-left]){
                //不构成逆序对,直接左侧元素覆盖nums[k]
                nums[k]=aux[i-left];
                i++;
            }else{
                //右区间元素>左区间元素,构成逆序对
                //此时逆序对个数:mid-i+1(j小于i说明小于i到mid之间所有元素【i->mid的元素有序】)
                res+=(mid-i)+1;
                res %= 1000000007;
                //右边小于左边,右边元素往nums[k]上覆盖
                nums[k]=aux[j-left];
                j++;
            }
        }
        return res;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值