首先我们来说下逆序对的概念
比如数组{1,2,3,4}它的逆序对数为0,数组{4,3,2,1}的逆序对数为6,分别是(4,3),(4,2),(4,1),(3,2),(3,1),(2,1);通过这两个举例我想大家应该明白逆序对的定义了,也就是前面的数比后面的数小就构成一个逆序对。接下来就是如何统计逆序对的问题了。
第一种做法我们可以使用暴力枚举法,通过一个简单的二重for循环可以轻松解决,但是这种做法时间复杂度是O(n²),并不高效。于是我们引出第二种做法那就是借助归并排序来实现。
归并排序
说到归并排序,那我们先来回忆下归并排序的算法思想,归并排序算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并操作的工作原理如下: 第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 。
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置 。
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 。
重复步骤三直到某一指针超出序列尾将另一序列剩下的所有元素直接复制到合并序列尾。
实现代码如下:
public static int[] mergeSort(int[] nums, int l, int h) {
if (l == h)
return new int[] {
nums[l] };
// 二分分组
int mid = l + (h - l) / 2;
int[] leftArr = mergeSort(nums, l, mid); // 左有序数组
int[] rightArr = mergeSort(nums, mid + 1, h); // 右有序数组
int[] newNum = new int[leftArr.length + rightArr.length]; // 新有序数组
// 左右数组进行比较
int m = 0, i = 0, j = 0;
// 合并到一个数组
// 直到有一方序列指针超出索引值,while循环结束
while (i < leftArr.length && j < rightArr.length) {
if (leftArr[i] < rightArr[j]) {
newNum[m++] = leftArr[i++];
} else {
newNum[m++] = rightArr[j++];
}
}
// 右边数组指针超出数组,直接把左边数组剩余元素直接全部复制到合并数组尾部
while (i < leftArr.length)
newNum[m++] = leftArr[i++];
// 左边数组指针超出数组,直接把右边数组剩余元素直接全部复制到合并数组尾部
while (j < rightArr.length)
newNum[m++] = rightArr[j++];
return newNum;
}
利用归并排序解决逆序对问题
下面来谈谈怎么用归并排序来求解我们的逆序对问题
比如说数组{2,1,4,3}在使用归并排序的时候分组会先分为两组即{2,1}和{4,3},接着继续分为四组{2},{1},{4},{3},首先是{2}和{1}相比较,1<2,于是合并数组里先