文章目录
题目链接:
题目描述:

解法
归并排序(分治)
当前元素的后面,有多少个比我小。(降序)

C++ 算法代码:
cpp
class Solution
{
vector<int> ret; // 存储结果:每个元素右侧小于当前元素的个数
vector<int> index; // 记录 nums 中当前元素的原始下标,用于追踪元素位置
int tmpNums[500010]; // 临时数组,用于归并排序中合并两个子数组
int tmpIndex[500010]; // 临时数组,用于保存合并后的索引顺序
public:
vector<int> countSmaller(vector<int>& nums)
{
int n = nums.size();
ret.resize(n); // 初始化结果数组大小,默认值都是0
index.resize(n); // 初始化索引数组大小
// 初始化索引数组,记录每个元素的原始位置
for(int i = 0; i < n; i++)
index[i] = i;
mergeSort(nums, 0, n - 1); // 对整个数组进行归并排序
return ret; // 返回结果数组
}
void mergeSort(vector<int>& nums, int left, int right)
{
if(left >= right) return; // 基本情况:如果区间只有一个元素或为空,则直接返回
// 1. 根据中间元素,划分区间
int mid = (left + right) >> 1; // 计算中间位置,相当于 (left + right) / 2
// 2. 递归地处理左右两部分
mergeSort(nums, left, mid); // 排序左半部分
mergeSort(nums, mid + 1, right); // 排序右半部分
// 3. 合并两个有序子数组,同时计算右侧小于当前元素的个数
int cur1 = left, cur2 = mid + 1, i = 0; // cur1指向左子数组,cur2指向右子数组,i遍历辅助数组
while(cur1 <= mid && cur2 <= right) // 当两个子数组都还有元素时
{
if(nums[cur1] <= nums[cur2]) // 如果左子数组当前元素小于等于右子数组当前元素
{
tmpNums[i] = nums[cur2]; // 将右子数组的元素放入临时数组
tmpIndex[i++] = index[cur2++]; // 同时记录对应的原始索引
}
else // 如果左子数组当前元素大于右子数组当前元素
{
// 核心逻辑:此时右子数组中从cur2到right的所有元素都小于当前的nums[cur1]
ret[index[cur1]] += right - cur2 + 1; // 将这些元素的数量累加到结果中
tmpNums[i] = nums[cur1]; // 将左子数组的元素放入临时数组
tmpIndex[i++] = index[cur1++]; // 同时记录对应的原始索引
}
}
// 4. 处理剩余元素
while(cur1 <= mid) // 处理左子数组中剩余的元素
{
tmpNums[i] = nums[cur1];
tmpIndex[i++] = index[cur1++];
}
while(cur2 <= right) // 处理右子数组中剩余的元素
{
tmpNums[i] = nums[cur2];
tmpIndex[i++] = index[cur2++];
}
// 5. 将临时数组中的元素复制回原数组
for(int j = left; j <= right; j++)
{
nums[j] = tmpNums[j - left]; // 更新原数组中的元素值
index[j] = tmpIndex[j - left]; // 更新原数组中元素对应的原始索引
}
}
};
图解
例如:nums = [5, 2, 6, 1]
-
初始化:
-
ret = [0, 0, 0, 0]
(结果数组,初始全为0) -
index = [0, 1, 2, 3]
(原始索引数组)
-
-
第一次划分:
- 将
[5, 2, 6, 1]
分为[5, 2]
和[6, 1]
- 将
-
处理左半部分
[5, 2]
:-
进一步划分为
[5]
和[2]
-
这些是单个元素,不再划分
-
合并
[5]
和[2]
(降序合并):- 比较 5 和 2,5 > 2,
nums[cur1] > nums[cur2]
ret[index[cur1]] += right - cur2 + 1;
- 因为左边元素大,所以选择左边元素,
ret
不变 - 合并后,左半部分变为
[5, 2]
,索引变为[0, 1]
- 比较 5 和 2,5 > 2,
-
-
处理右半部分
[6, 1]
:-
进一步划分为
[6]
和[1]
-
这些是单个元素,不再划分
-
合并
[6]
和[1]
(降序合并):- 比较 6 和 1,6 > 1
- 因为左边元素大,所以选择左边元素,
ret
不变 - 合并后,右半部分变为
[6, 1]
,索引变为[2, 3]
-
-
最后合并
[5, 2]
和[6, 1]
(降序合并):- 比较 5 和 6: 5 <= 6,选择右侧元素6,
ret
不变 - 比较 5 和 1: 5 > 1,这时右子数组中只有1比5小,所以
ret[index[cur1]] += right - cur2 + 1 → ret[0] += 3 - 3 + 1 → ret[0] = 1
- 比较 2 和 1: 2 > 1,右子数组中只有1比2小,所以
ret[index[cur1]] += right - cur2 + 1 →ret[1] += 3 - 3 + 1 → ret[1] = 1
- 比较 5 和 6: 5 <= 6,选择右侧元素6,
-
最终结果:
ret = [2, 1, 1, 0]


3.第一次循环:while(cur1 <= mid && cur2 <= right)

4.第二次循环:while(cur1 <= mid && cur2 <= right)

5.第三次循环:while(cur1 <= mid && cur2 <= right)

6.处理剩余元素while(cur2 <= right)
