- 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1
输入: [7,5,6,4]
输出: 5
示例 2
输入: [7,5,6,4,7,6,8,5,6,4,5,6,7,8,9,4,5,3,2,1,3,4,5,7]
输出: 154
题解思路
- 通过题意,首先可能会想到的是对数组中的单个元素进行一一比较,如此我们就轻轻松松掉进了圈套,代码如下; 提交后发现会超时,次算法的时间复杂度为 O(n2),所以我们应该继续对时间进行优化。
public int reversePairs(int[] nums) {
int n = nums.length
int total = 0
for (int i = 0
for (int j = i + 1
if (nums[i] > nums[j]) total++
}
}
return total
}
- 通过题意对比,可发现在问题求解的过程中与归并排序有着非常巧妙地相似度,所以可以尝试着分析。归并排序主要是 分解 和 合并 两个过程,而每一次分解都会将当前数组分解为两个更小的子数组,每一次合并都将两个子数组合并成一个数组。例如(官方示例)
- 假设我们有两个已排序的序列等待合并,分别是 L = { 8,12,16,22,100 } 和 R = { 9, 26, 55, 64, 91 }。一开始我们用指针 p1 = 0 指向 L 的首部,p2 = 0 指向 R 的头部。对于两个已经排好序的子数组,我们找出右子数组单个元素符合逆序对的个数,若 L + R 的长度为 n, 则时间复杂度,为 O(n)。相对于之前的暴力算法已经得到了大大的提升。
L = [8, 12, 16, 22, 100] R = [9, 26, 55, 64, 91]
| |
p1 p2
- 依照上面的思路,可得出以下代码。本题本质上是对归并排序的应用,只要对归并排序有一定的理解就可以解决问题了。
public int reversePairs(int[] nums) {
return mergeSort(nums)
}
int[] T
private int mergeSort(int[] nums) {
int n = nums.length
T = new int[n]
return sort(nums,0,n - 1)
}
private int sort(int[] nums, int l, int r) {
if (l >= r) return 0
int m = l + (r - l) / 2
int left = sort(nums,l,m)
int right = sort(nums,m + 1,r)
int count = merge(nums,l,m,r)
return count + left + right
}
private int merge(int[] nums, int l, int m, int r) {
int l1 = l
int l2 = m + 1
int count = 0
for (int i = l
for (int i = l
if (l1 > m) nums[i] = T[l2++]
else if (l2 > r) nums[i] = T[l1++]
else if (T[l1] <= T[l2]) nums[i] = T[l1++]
else {
count += m + 1 - l1
nums[i] = T[l2++]
}
}
return count
}