问题:给定一个长度为n的排列,求其逆序对数
输入格式:
输入一个数组
输出格式:
输出逆序对总数
样例输入:
[1,2,4,3]
样例输出:
1
数据规模:
n<=100000
解题思路:
首先,在解决这个问题之前,先了解一下什么叫逆序对
逆序对:数学术语,设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。
简单来说,就是在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
例如:
[1,2,4,3]
其中的逆序对只有(4,3)
所以return 1
在例如:
数组 [2, 4, 1, 3, 5] 中
有 3 个逆序对 (2, 1), (4, 1), (4, 3)
则 return 3
知道了逆序对是什么,就很好的求解了,只需要要左边的大于右边的即可。
那么,用归并的思想来说,在每一次拆分合并的过程中,可以比较左边的数组和右边的两个数组,在组成新的数组中,每次右边的数组加入新数组,那么就是一组逆序对(因为左边的大于右边的)。
不知道的归并排序的小伙伴可以看看我另一篇文章
经典排序之一:归并排序
例如:nums=[2,9,5,4]
其过程如图:
由上图可明显的看出,在每次归并的过程中当右边的数组加入新数组的时候,那么此时的逆序对个就要相加。
注意:这里的相加不是简单的加1,而是加上左边数组的长度减去当前下标。因为左边最小的数子已经大于右边,那么,左边数组的所有数必然大于右边的数,每一个都是一个逆序对。
然后,在每次递归调用将其逆序对加起来,就是所求的总逆序对数。
这就是归并排序的思想,很简单,只需要增加一行代码即可。
代码我放下下面,小伙伴们可以去看看我另一篇文章中归并排序的代码,两个一对比,相信你很快能理解。
题目代码:
public class CastleWalls {
static long revers = 0; //一定要用long int相对于100000个数时候会溢出,返回一个负号
private static void mergeSort(int[] list) {
if (list.length > 1) {
int[] fiestHalf = new int[list.length / 2];
System.arraycopy(list, 0, fiestHalf, 0, list.length / 2);
mergeSort(fiestHalf);
int[] secondHalf = new int[list.length - list.length / 2];
System.arraycopy(list, list.length / 2, secondHalf, 0, list.length - list.length / 2);
mergeSort(secondHalf);
merge(fiestHalf, secondHalf, list);
}
}
private static void merge(int[] list1, int[] list2, int[] list3) {
int current1 = 0;
int current2 = 0;
int current3 = 0;
while (current1 < list1.length && current2 < list2.length) {
if (list1[current1] <= list2[current2]) { ///必须小于等于,当两个比较相等,其后面就不用比较,将左边的放入数组,
// 然后右边的继续比较左边的,校友,则为逆序数,将其放入新数组
list3[current3++] = list1[current1++];
} else {
list3[current3++] = list2[current2++];
revers += (list1.length - current1); //如果左边最小的数已经大于右边。那么左边的所有数都大于右边,那么就组成了list1.lenth-current1个逆序对
}
}
while (current1 < list1.length) {
list3[current3++] = list1[current1++];
}
// for (int i = current1; i < list1.length; i++) {
// list3[current3++] = list1[i];
// }
while (current2 < list2.length) {
list3[current3++] = list2[current2++];
}
// for (int i = current2; i < list2.length; i++) {
// list3[current3++] = list2[i];
// }
}
public static void main(String[] args) {
//可根据输入N 来输入数组规模
// Scanner input = new Scanner(System.in);
// int lenth = input.nextInt();
// int [] num = new int[lenth];
// for (int i = 0; i < num.length; i++) {
// num[i]=input.nextInt() ;
// }
// mergeSort(num);
// System.out.println(revers);
//测试数组
int num[] = {3, 8, 6, 11, 2, 7, 11};
mergeSort(num);
System.out.println(Arrays.toString(num));
System.out.println(revers);
}
}
测试结果:
[2, 3, 6, 7, 8, 11, 11]
7