剑指 Offer 51.数组中的逆序对
不能用暴力解法!
归并排序:
在分的过程中将数组拆分到只有一个元素,在治的过程中排序再合并,并且统计逆序对的数量。
与归并排序唯一的区别就是加了逆序对的计算,那么怎么计算逆序对呢?
当右边的A小于左边的B时,那么A会小于B右边到右边界所有的数,这些数的数量为mid-B的下标+1
将每一次归并时这些数量统计起来即为结果
class Solution {
public int reversePairs(int[] nums) {
if(nums.length <= 1) { //如果只有一个数直接返回0
return 0;
}
int[] copy = new int[nums.length]; //不改变原数组,复制一份
for(int i = 0; i < nums.length; i++) {
copy[i] = nums[i];
}
int[] temp = new int[nums.length]; //用来存每一轮还没合并前的值,用来比较
return reversePairs(copy, 0, nums.length - 1,temp);
}
public int reversePairs(int[] nums, int left, int right, int[] temp) {
if(left == right) return 0; //拆分到只有一个数的时候返回0
int mid = left + (right - left) / 2; //安全的求中点
int leftPairs = reversePairs(nums, left, mid, temp); //记录左边的逆序对的数量并排序
int rightPairs = reversePairs(nums, mid + 1, right, temp); //记录右边的逆序对的数量并排序
if(nums[mid] <= nums[mid + 1]) return leftPairs + rightPairs; //如果左边最后一个数小于等于右边第一个数,就直接返回
int crossPairs = mergeandCount(nums, left, right, mid, temp); //记录跨越左右两边的逆序对的数量
return leftPairs + rightPairs + crossPairs;
}
public int mergeandCount(int[] nums, int left, int right, int mid, int[] temp) {
for(int i = left; i <=right; i++) {
temp[i] = nums[i]; //复制nums
}
int i = left; //左边第一个数
int j = mid + 1; //右边第一个数
int count = 0;
for(int k = left; k <= right; k++) {
if(i == mid + 1) { //如果左边都搜索完了,就直接把右边的加上去
nums[k] = temp[j];
j++;
}
else if(j == right + 1) { //如果右边都搜索完了,就直接把左边的加上去
nums[k] = temp[i];
i++;
}
else if(temp[i] <= temp[j]) { //如果左边的数小于右边,就把左边的数添加进去
nums[k] = temp[i];
i++;
}
else { //如果右边的数小于左边,就把右边的数添加进去,说明右边的数大于左边剩下的所有的数,将左边剩下的数加起来
nums[k] = temp[j];
j++;
count += mid - i + 1;
}
}
return count;
}
}