力扣链接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
eg. 输入: [7, 5, 6, 4] ; 输出: 5
思路:归并排序
=> 对于两个已经排序的数组,如何利用归并排序寻找“跨数组”的逆序对?(如下面的 (2,1) (5, 1) (5, 4))
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: []
count: 0
此时两个指针指向的数字2>1,由于两个数组都是有序的,如果2比1大,那么左数组中,排在2之后的元素,一定也都比1大。 则左数组中所有可以与1组成逆序对的元素个数为4,count+4。
现在与1配对的逆序对已经找完,我们将rPtr右移,继续找与4配对的:
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: [1]
count: 4
此时 2<4,由于两个数组都是有序的,如果2不能与4构成逆序对,也不可能与4之后的元素构成,包含2的逆序对已经找全,我们将lPtr右移:
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: [1, 2]
count: 4
此时 3<4,与刚才同理,包含3的逆序对也已经找全(不存在),继续将lPtr右移:
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: [1, 2, 3]
count: 4
此时出现 5>4, 则左数组中排在5以后的元素都可以与4构成逆序对,count+2,4的逆序对已找全,右移rPtr:
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: [1, 2, 3, 4]
count: 6
5<6,右移lPtr:
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: [1, 2, 3, 4, 5]
count: 6
7>6, count+1, 右移rPtr:
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: [1, 2, 3, 4, 5, 6]
count: 7
7<8,右移lPtr:
[2, 3, 5, 7] [1, 4, 6, 8]
0 1 2 3 0 1 2 3
lPtr rPtr
sorted: [1, 2, 3, 4, 5, 6, 7]
count: 7
左边数组已经遍历结束,将右数组剩余元素写入sorted: [1, 2, 3, 4, 5, 6, 7, 8],归并排序结束,总计逆序对总数count为7。
由此可见,我们只需要在归并排序的归并阶段,加入上述统计逆序对个数的步骤即可。
代码:
/** * @param {number[]} nums * @return {number} */ var reversePairs = function(nums) { return mergeSort(nums, 0, nums.length-1); }; var mergeSort = function(nums, left, right){ if(left >= right) return 0; let middle = Math.floor((right + left) / 2); let lCount = mergeSort(nums, left, middle); let rCount = mergeSort(nums, middle + 1, right); return lCount + rCount + mergeNCount(nums, left, middle, right); } var mergeNCount = function(nums, left, middle, right){ let lPart = []; let rPart = []; let lSize = middle - left + 1; let rSize = right - middle; for(let li = 0; li < lSize; li++){ lPart.push(nums[li + left]); } for(let ri = 0; ri < rSize; ri++){ rPart.push(nums[ri + middle + 1]); } let lPtr = 0, rPtr = 0, p = left; let count = 0; while(lPtr < lSize && rPtr < rSize){ if(lPart[lPtr] > rPart[rPtr]){ count += lSize - lPtr; nums[p++] = rPart[rPtr++]; } else { nums[p++] = lPart[lPtr++]; } } while(lPtr < lSize){ nums[p++] = lPart[lPtr++]; } while(rPtr < rSize){ nums[p++] = rPart[rPtr++]; } return count; }
原文:https://www.cnblogs.com/xintangchn/p/14675263.html