题目描述
分析
这题和逆序对那道题目实际上是一个思路,但是需要注意返回结果
本体要求的是每个 index 后边有多少个元素比他小,而不是一共有多少个逆序对,所以会更困难一些~
解题思路
在归并的合并阶段,统计个数
算法
归并排序(降序)
思路
利用归并排序,在合并过程中进行统计
统计过程
对于左边的一个元素 arr[i],若归并的是两个降序区间,如果找到了右区间的第一个符合 nums[i] > nums[j] 的元素,则说明对于 arr[i] 来说,右 r - p2 + 1 个逆序对,即元素比他小的个数
r 为右区间右边界
p2 为右区间左边界
归并的特殊处理
题目要求记录一个 index 的情况,所以我们不能只是对原数组排序,要在归并中知道每个元素再原数组里的 index,所以我的处理是把原数组 map 成每项为 {val, index} 的形式来保存两个信息:
val: 值,
index: 在原数组的索引
过程
对原数组做 map
把每个 item 处理成上述形式,我将处理后的数组称为 arr
归并排序
归并排序的样板就忽略了,关键看怎么统计
在递归完左,右区间之后,在合并的过程中,需要对要合并 p1 指针元素的时候进行统计,即:
if (p2 > r || (p1 <= mid && (sum[p1].val > sum[p2].val))) {
ret[sum[p1].index] += r - p2 + 1
temp[k++] = sum[p1++]
}
注意 ⚠️:我们每个 item 都是一个对象,所以取值和取index的过程需要特别注意
代码
/**
* @param {number[]} nums
* @return {number[]}
*/
var countSmaller = function (nums) {
const arr = []
for (let i = 0; i < nums.length; i++) {
arr.push({ val: nums[i], index: i })
}
return mergeSort(arr, 0, arr.length - 1, new Array(arr.length).fill(0))
}
function mergeSort(arr, l, r, ret) {
if (l >= r) return ret
const mid = (l + r) >> 1,
temp = []
let p1 = l,
p2 = mid + 1,
k = 0
mergeSort(arr, p1, mid, ret)
mergeSort(arr, p2, r, ret)
while (p1 <= mid || p2 <= r) {
if (p2 > r || (p1 <= mid && arr[p1].val > arr[p2].val)) {
ret[arr[p1].index] += r - p2 + 1
temp[k++] = arr[p1++]
} else {
temp[k++] = arr[p2++]
}
}
for (let i = l; i <= r; i++) {
arr[i] = temp[i - l]
}
return ret
}