计算右侧小于当前元素的个数
使用归并排序的思路,详见 15. 归并排序 总结部分
主要在 merge
函数,我们在使用 merge
函数合并两个有序数组的时候,其实是可以知道一个元素 nums[i]
后边有多少个元素比 nums[i]
小的
var countSmaller = function (nums) {
// 新增:创建计数数组和带索引的数组
const counts = new Array(nums.length).fill(0);
// 将原数组转换为带索引的数组,方便排序时记录原始位置
const indexedNums = nums.map((val, idx) => ({ val, idx }));
function sort(nums) {
let n = nums.length;
if (n < 2) return nums;
// 递归划分阶段
const mid = Math.floor(n / 2);
let leftArr = nums.slice(0, mid);
let rightArr = nums.slice(mid);
let left = sort(leftArr);
let right = sort(rightArr);
// 后序位置:合并阶段
// 这时候是有序的,所以方便:计算右侧小于当前元素的个数
return mergeSortArr(left, right);
}
function mergeSortArr(arr1, arr2) {
let m = arr1.length;
let n = arr2.length;
let left = 0;
let right = 0;
let res = [];
// 修改:在合并时统计右侧小于当前元素的个数
while (left < m && right < n) {
if (arr1[left].val <= arr2[right].val) {
// 新增:当左边元素小于等于右边元素时,
// 将右边已经遍历过的小于当前元素的个数加入结果
counts[arr1[left].idx] += right;
res.push(arr1[left]);
left++;
} else {
res.push(arr2[right]);
right++;
}
}
// 修改:处理剩余的左半部分,这些元素都大于右边已经处理的元素
while (left < m) {
// 新增:将右边所有元素的个数加入结果
counts[arr1[left].idx] += right;
res.push(arr1[left]);
left++;
}
// 处理剩余的右半部分
while (right < n) {
res.push(arr2[right]);
right++;
}
return res;
}
// 新增:调用排序函数并返回结果
sort(indexedNums);
return counts;
};