剑指Offer-题51(Java版):数组中的逆序对

参考自:《剑指Offer——名企面试官精讲典型编程题》

题目:数组中的逆序对
在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

主要思路
  类似于归并排序。先把数组等分成子数组,统计出子数组的逆序对数目,在统计子数组的过程中,同时对子数组进行从小到大排序。然后,递归统计前后相邻子数组之间的逆序对数目。
  统计前后相邻子数组逆序对的过程如下:使用两个指针分别指向前后子数组的末尾,然后比较指针指向的数字大小。若前面的数字大于后面的数字,说明有逆序对,且逆序对的数目等于后面数组剩余数字的数目(因为,后半段剩余的数字都小于或等于后半段指针指向的数字)。若前面的数字小于或等于后面的数字,说明无逆序对。每次比较完,都把较大的数字从后向前复制到辅助数组,保证辅助数组按从小到大排序,有序的辅助数组将用于求下一次更大的相邻子数组逆序对数目。
  这个过程就相当于在归并排序的过程中,统计逆序对的数目。

关键点:归并排序,递归

时间复杂度:O(nlogn)

public class InversePairs
{
    public static void main(String[] args)
    {
        int[] data = {1, 2, 3, 4, 7, 6, 5};
        int[] data1 = {2, 1};
        System.out.println(getInversePairsCount(data)); //3
        System.out.println(getInversePairsCount(data1)); //1
    }

    private static long getInversePairsCount(int[] data)
    {
        if (data == null || data.length == 0) return 0;
        int length = data.length;
        int[] copy = new int[length];
        //复制数组到辅助数组
        System.arraycopy(data, 0, copy, 0, length);
        long count = inversePairsCount(data, copy, 0, length - 1);
        return count;
    }

    private static long inversePairsCount(int[] data, int[] copy, int start, int end)
    {
        //子数组只有一个数
        if (start == end)
        {
            copy[start] = data[start];
            return 0;
        }

        int middle = start + (end - start) / 2;

        //copy和data数组在这里交换位置
        //相当于每次递归后data的前后半段都是有序的
        long leftCount = inversePairsCount(copy, data, start, middle);
        long rightCount = inversePairsCount(copy, data, middle + 1, end);

        //前半段最后一个数字的下标
        int leftLast = middle;
        //后半段最后一个数字的下标
        int rightLast = end;
        //从后向前复制数字到辅助数组,保证从小到大排序
        int indexCopy = end;
        long count = 0;
        //从后向前遍历
        while (leftLast >= start && rightLast >= middle + 1)
        {
            //前半段指针指向的数字大于后半段指针指向的数字
            if (data[leftLast] > data[rightLast])
            {
                copy[indexCopy--] = data[leftLast--];
                //逆序对的数目等于后半段剩余数字的个数
                //因为,后半段剩余的数字都小于或等于后半段指针指向的数字
                count += rightLast - middle;
            } else
            {
                copy[indexCopy--] = data[rightLast--];
            }
        }

        //复制子数组剩余的数字
        while (leftLast >= start)
        {
            copy[indexCopy--] = data[leftLast--];
        }
        while (rightLast >= middle + 1)
        {
            copy[indexCopy--] = data[rightLast--];
        }

        return leftCount + rightCount + count;
    }

}

你可能感兴趣的:(剑指Offer-Java)