剑指Offer面试题51:数组中的逆序对

在做这道题的时候,第一个想到的是从前往后一个一个遍历,找逆序对,时间复杂度是n^2,肯定不会被通过。

随后发现,逆序关系,是与数的大小有关,可以通过排序来进行,但是排序有一个问题,就是数位置变换过后,对于随后的数字的计算,几乎不能实现。

看了答案的思路,答案解决这个问题的思路是,将数组拆分成长度为一的相邻数组,对于相邻的,判断大小,统计逆序,并且合并,在合并过后,由于这两个数的顺序,不会影响其他在这两个数之外的逆序统计值,因此将这两个数进行排序,然后将已经合并的数组,再递归进行两两合并排序。

答案代码的递归调用,copy数组和data数组递归一次,交换一次,很难理解。

因为每一层都是对data里的数据进行操作,合并到copy里,因此,递归的时候交替调用,就意味着当前函数调用的data,是子递归的copy,子递归copy过后得到合并好的数据作为父递归的data,然后子递归的data作为容器保存父递归的copy数组经由计算得到的值。

    public int reversePairs(int[] nums) {
        if(nums.length == 0){
            return 0;
        }  
        int[] copy = new int[nums.length];
        for(int count = 0;count<nums.length;count++){
            copy[count] = nums[count];
        }

        int res = reversePairsDivide(nums,copy,0,nums.length-1);
        return res;
    
    }
    public int reversePairsDivide(int[] data,int[] copy,int start,int end) {
        if(start == end){
            return 0;
        }
        int data_length = end - start;
        int mid = data_length/2;
        int left = reversePairsDivide(copy,data,start,start+mid);
        int right = reversePairsDivide(copy,data,start+mid+1,end);
        int i = start + mid;
        int j = end;
        int copyIndex = end;
        int res = 0;
        while(i>= start&& j>=start + mid + 1){
            if(data[i]>data[j]){
                res += (j - start - mid);
                copy[copyIndex--] = data[i--];
            }else{
                copy[copyIndex--] = data[j--];
                
            }
        }

        for(;i>=start;){
            copy[copyIndex--] = data[i--];
        }

        for(;j>=start + mid + 1;){
            copy[copyIndex--] = data[j--];
        }
        return res + left + right;
    }

猜你喜欢

转载自blog.csdn.net/qq_40473204/article/details/114636518