面试题51:数组的逆序对

2019-11-12  本文已影响0人  繁星追逐

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。

思路:使用归并的方式分治查找递归,归并完已排序好的,然后合并时分组比较。两组数据,设置两个指针分别从排序序列最后一个元素开始,分别为mid,high,依次进行比较,如果前者位置的数据大于后者,则满足逆序对条件,因为第二个序列有序,所以最后一个元素满足,必然前面所有的元素的都满足,因此数据其中有j-mid位逆序对,完成后将第一个指针前移,如果前者小于等于后者,后一个指针再前移。同时如果有一个指针过界,则装下另外一个元素的位置。
注意调整顺序的元素都在原数组上做改动。建一个一摸一样的新的用来比较的数组。
代码如下:

public int InversePairs1(int [] array) {
       if (array == null || array.length == 0) return 0;
       int[] aux = new int[array.length];

       return sort(array, aux, 0, array.length-1);

    }

    private int sort(int[] array, int[] aux, int low, int high) {

        //边界
        if (low >= high) return 0;
        int mid = low + (high-low)/2;
        ///分为两个范围
        int left = sort(array, aux, low, mid);
        int right = sort(array, aux, mid+1, high);
        //传入分界点作为第一个指针默认值
        int merge = mergePairs(array, aux, low, mid, high);

        return left+right+merge;

    }

    private int mergePairs(int[] array, int[] aux, int low, int mid, int high) {
        int count = 0;
        int len = (high-low)/2;
        //mid是一个数组的最后一个值的索引
        int i = mid;
        int j = high;

        //递归起始点不是0,有边界
        for (int m=low;m<array.length;m++){
            aux[m] = array[m];
        }
        //起始点是low
        for (int k=high;k>=low;k--){
            if (i < low) array[k] = aux[j--];
            //第二个数组从mid+1开始
            else if (j < mid+1) array[k] = aux[i--];
            else if (aux[i] > aux[j]){
                //每次累加,当得到第一个数组中的一个数比第二个数组对应位置大时,因为数组有序,所以二数组对应位置之前的所有值都是满足逆序对的
                count += j - mid;
                array[k] = aux[i--];
            }else {
                array[k] = aux[j--];
            }
        }
        return count;
    }
    //设置全局变量记录数量
    private int cnt = 0;

    public int InversePairs(int[] array) {
        if (array == null || array.length == 0) return 0;

        mergeSortUp2Down(array, 0, array.length-1);
        return cnt;

    }

    /*
     * 归并排序(从上往下)
     */
    public void mergeSortUp2Down(int[] a, int start, int end) {
        if (start >= end) return;
        //右移表示除以2
        int mid = (start + end) >> 1;

        mergeSortUp2Down(a, start, mid);
        mergeSortUp2Down(a, mid+1, end);
        merge(a, start, mid, end);
    }

    /*
     * 将一个数组中的两个相邻有序区间合并成一个
     */
    public void merge(int[] a, int start, int mid, int end) {
        int[] temp = new int[end - start + 1];
        int i = start;
        int j = mid+1;
        int k = 0;
        while (i <= mid && j <= end){
            if (a[i] <= a[j]){
                temp[k++] = a[i++];
            }else {
                cnt += mid - i +1;
                temp[k++] = a[j++];
            }
        }
        //其中一个结束了
        while (i <= mid){
            temp[k++] = a[i++];
        }
        while (j <= end){
            temp[k++] = a[j++];
        }
        //排序后的重新赋给相应位置的原数组
        for (int m=0;m<temp.length;m++){
            a[start+m] = temp[m];
        }

    }
上一篇下一篇

猜你喜欢

热点阅读