java实现——数组中的逆序对(剑指offer原题)

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

牛客上的题目添加了一个取模要求,不妨碍,只需要在求的数目后取模即可。

这道题就是归并排序的应用,复习以下归并排序~

public class MergeSortTest {
    public static void main(String[] args) {
        int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
        print(data);
        mergeSort(data);
        System.out.println("排序后的数组:");
        print(data);
    }
    public static void mergeSort(int[] data) {
        sort(data, 0, data.length - 1);
    }
    public static void sort(int[] data, int left, int right) {
        if (left >= right)
            return;
        // 找出中间索引
        int center = (left + right) / 2;
        // 对左边数组进行递归
        sort(data, left, center);
        // 对右边数组进行递归
        sort(data, center + 1, right);
        // 合并
        merge(data, left, center, right);
        print(data);
    }
    /*
    * @param data
    * 数组对象
    * @param left
    * 左数组的第一个元素的索引
    * @param center
    * 左数组的最后一个元素的索引,center+1 是右数组第一个元素的索引
    * @param right
    * 右数组最后一个元素的索引
    */
    public static void merge(int[] data, int left, int center, int right) {
    // 临时数组
        int[] tmpArr = new int[data.length];
    // 右数组第一个元素索引
        int mid = center + 1;
    // third 记录临时数组的索引
        int third = left;
    // 缓存左数组第一个元素的索引
        int tmp = left;
        while (left <= center && mid <= right) {
    // 从两个数组中取出最小的放入临时数组
            if (data[left] <= data[mid]) {
                tmpArr[third++] = data[left++];
            } else {
                tmpArr[third++] = data[mid++];
            }
        }
    // 剩余部分依次放入临时数组(实际上两个 while 只会执行其中一个)
        while (mid <= right) {
            tmpArr[third++] = data[mid++];}
        while (left <= center) {
            tmpArr[third++] = data[left++];
        }
    // 将临时数组中的内容拷贝回原数组中
    // (原 left-right 范围的内容被复制回原数组)
        while (tmp <= right) {
            data[tmp] = tmpArr[tmp++];
        }
    }
    public static void print(int[] data) {
        for (int i = 0; i < data.length; i++) {
            System.out.print(data[i] + "\t");
        }
        System.out.println();
    }

}

输出结果:

5	3	6	2	1	9	4	8	7	
3	5	6	2	1	9	4	8	7	
3	5	6	2	1	9	4	8	7	
3	5	6	1	2	9	4	8	7	
1	2	3	5	6	9	4	8	7	
1	2	3	5	6	4	9	8	7	
1	2	3	5	6	4	9	7	8	
1	2	3	5	6	4	7	8	9	
1	2	3	4	5	6	7	8	9	
排序后的数组:
1	2	3	4	5	6	7	8	9

归并排序平均时间复杂度O(nlogn),并且稳定
回到题目上,就是排序在合并时候进行统计个数,理解归并排序后结合注解就可以明白

public class test1 {
    public int result=0;
    public int InversePairs(int [] array) {
        mergeCount(array,0,array.length-1);
        return result;
    }
    public void mergeCount(int[] nums, int left, int right){
        if (left>=right) return;
        int mid=(right+left)/2;
        mergeCount(nums,left,mid);
        mergeCount(nums,mid+1,right);
        merge(nums,left,mid,right);
    }

    private void merge(int[] nums, int left, int mid, int right) {
        int [] temp=new int[right-left+1];
        //mid是第一个数组的最后 j是第二个数组的开始
        int k=0,i=left,j=mid+1;
        while(i<=mid&&j<=right)
        {
            //从左右两个数组中 前面的小于后面的 不能构成逆序对
            if (nums[i]<=nums[j])
            {
                //把小的放进入
                temp[k++]=nums[i++];
            }
            //前面的大于后面的  说明前面数组之后的所有数都能和后面数组的这意味构成逆序对 那么算出前面数组元素的个数即可 mid-i+1
            else{
                temp[k++]=nums[j++];
                result=(result+(mid-i+1))%1000000007;
            }
        }
        //把两个数组剩余元素放入辅助数组 所有两个数组的循环只会执行一个 必定有一个已经被放完
        while(i<=mid)
            temp[k++]=nums[i++];
        while(j<=right)
            temp[k++] = nums[j++];
        //将临时数组的元素放入原来的数组中
        for (int m=0;m<k;m++)
            nums[left+m]=temp[m];
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值