【剑指Offer】- 数组中的逆序对
题目链接:剑指Offer51
题目描述:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
限制:
0 <= 数组长度 <= 50000
题解:
解题思路:
在排序过程中统计逆序对的数量。
归并排序:
1.递归划分。计算数组的中点middle,递归划分左数组:left = nums.slice(0, middle),递归划分右数组:right = nums.slice( middle)。递归终止条件:当数组长度为1时,终止递归。
2.合并。创建辅助数组res,设置指针cur,i,j分别指向res,左,右数组。
1)当res>left.len,左子数组已合并完,只需添加right到res中;
2)当res > right.len, 右子数组已合并完,只需添加left到res中;
3)左右数组均未合并完,比较左右数组值的大小,若左子数组<右子数组,不存在逆序,合并数组;若左子数组 > 右子数组,则存在逆序。由于子数组已有序,所以当前left[i]之后的值均 > right[j],所以逆序长度为left.len - i。
/**
* @param {number[]} nums
* @return {number}
*/
var reversePairs = function(nums) {
let sum = 0;
mergeSort(nums);
//归并排序
function mergeSort(nums){
if(nums.length <= 1)
return nums;
let middle = Math.floor(nums.length / 2);
let left = nums.slice(0, middle);
let right = nums.slice(middle, nums.length);
return merge(mergeSort(left), mergeSort(right));
}
//两两合并
function merge(left, right){
let len = left.length + right.length;
let res = new Array(len).fill(0);
for(let cur = 0, i = 0, j = 0; cur < len; cur++)
{
if(i >= left.length)
res[cur] = right[j++];
else if(j >= right.length)
res[cur] = left[i++];
else if(left[i] <= right[j])
res[cur] = left[i++];
else
{
res[cur] = right[j++];
// left[i] > right[j]的情况, 即left[i, end)和right[j]都是逆序对
sum += left.length - i;
}
}
return res;
}
return sum;
};