归并排序实战:计算右侧小于当前元素个数与翻转对
在算法面试中,涉及数组统计的问题往往需要高效的解决方案。今天我们来深入探讨两个经典的 LeetCode 题目,它们都可以通过**归并排序(Merge Sort)**的分治思想来解决。
49. 计算右侧小于当前元素的个数
题目描述
给定一个整数数组 nums,按要求返回一个新数组 counts。其中 counts[i] 表示在 nums[i] 右边且比它小的元素个数。
例如输入 [5, 2, 6, 1],输出应为 [2, 1, 1, 0]。
核心思路
这道题和求逆序对非常相似,但区别在于我们需要知道每个位置对应的具体数量,而不是总数。
归并排序在合并两个有序子数组时,如果左边的元素大于右边的元素,说明左边这个元素后面有若干个比它小的数。但在排序过程中,元素的原始下标会发生变化,因此我们需要维护一个辅助数组来记录每个元素原本的索引。
实现代码
class Solution {
public:
vector<int> counts;
vector<int> index; // 记录 nums 中当前元素的原始下标
vector<int> tmp;
vector<int> tmpi; // 临时记录合并后对应元素位置变化下标随之变化的数组
vector<int> countSmaller(vector<int>& nums) {
counts.resize(nums.size());
index.resize(nums.size());
tmp.resize(nums.size());
tmpi.resize(nums.size());
// 初始化 index,使得 nums 每个元素下标和 index 一一对应
for(int i = 0; i < nums.size(); i++) {
index[i] = i;
}
mergesort(nums, 0, nums.size() - 1);
return counts;
}
{
(left == right) {
;
}
mid = (right - left) / + left;
(nums, left, mid);
(nums, mid + , right);
cur1 = left, cur2 = mid + , i = ;
(cur1 <= mid && cur2 <= right) {
(nums[cur1] > nums[cur2]) {
counts[index[cur1]] += right - cur2 + ;
tmpi[i] = index[cur1];
tmp[i++] = nums[cur1++];
} {
tmpi[i] = index[cur2];
tmp[i++] = nums[cur2++];
}
}
(cur1 <= mid) {
tmpi[i] = index[cur1];
tmp[i++] = nums[cur1++];
}
(cur2 <= right) {
tmpi[i] = index[cur2];
tmp[i++] = nums[cur2++];
}
( j = left; j <= right; j++) {
nums[j] = tmp[j - left];
index[j] = tmpi[j - left];
}
}
};


