难度:Hard
题目:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4] 输出: 5
限制:
0 <= 数组长度 <= 50000
Related Topics
- 树状数组
- 线段树
- 数组
- 二分查找
- 分治
- 有序集合
- 归并排序
重点!!!解题思路
第一步:
明确解题手段:本章节主要练习归并排序所以本题使用归并排序来解决
第二步:
明确归并排序过程:
首先进行递归,然后进行合并,重复此步骤,即可完成归并排序
(1)但是本题要求求出逆序对,归并的过程就是分开再合并,当合并时左右两边都是升序
(2)如果右边第一个值小于左边第一个值,即代表右边第一个值和左边所有值都可以组成 逆序对
如果这里明白了 那么此题就可以解决了
源码+讲解:
class Solution {
int[] temp; //临时数组
public int reversePairs(int[] nums) {
if (nums.length < 2) return 0;
temp = new int[nums.length]; //临时数组必须大于等于原数组大小 用于拷贝
return merge_sort(nums, 0, nums.length - 1); //归并范围
}
public int merge_sort(int[] nums, int l, int r) {
if (l >= r) return 0; //终止条件 剩一个元素时不进行分组了
int mid = (l + r) >> 1, ans = 0; //求一个中间值mid,ans用于统计逆序对
ans += merge_sort(nums, l, mid); //递归分组
ans += merge_sort(nums, mid + 1, r); //递归分组
int p1 = l, p2 = mid + 1, k = l; //p1代表分组后左边的第一个值得下标,p2代表右边第一个值得下标,k代表临时数组得下标
while (p1 <= mid || p2 <= r) { //合并
if (p2 > r || (p1 <= mid && nums[p1] <= nums[p2])) { //右面没有值了或者左面还有值并且左面p1的值小于右面p2的值时,将p1位置的值放入临时数组
temp[k++] = nums[p1++];
} else { //同上
temp[k++] = nums[p2++];
ans += mid - p1 + 1; //当p2的值小于p1的时候,那么此时左面剩下的所有值都能和此时p2的值构成逆序对
}
}
for (int i = l; i <= r; i++) nums[i] = temp[i]; //将临时数组拷贝回原数组,以便递归排序
return ans;
}
}
运行结果:
系列持续更新中,喜欢练习算法的那就点个攒吧