参考自:《剑指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;
}
}