1. 题目解析
题目链接:315. 计算右侧小于当前元素的个数
这个问题的理解其实相当简单,只需看一下示例,基本就能明白其含义了。
2.算法原理
算法步骤
- 初始化:
- 定义两个全局数组:
index
用于存储原始数组元素的下标,ret
用于存储每个位置对应的逆序对数量。 - 在
countSmaller
函数中,初始化这两个数组,并为它们分配与输入数组nums
相同大小的空间。
- 定义两个全局数组:
- 归并排序与逆序对计数:
- 设计一个递归的归并排序函数
mergeSort
,该函数接受数组nums
、左边界left
和右边界right
。 - 在
mergeSort
函数中,首先检查递归的基本情况,即当left >= right
时直接返回。 - 否则,将数组划分为左右两个子数组,并递归地对它们进行归并排序和逆序对计数。
- 设计一个递归的归并排序函数
- 合并与计数:
- 当合并两个已排序的子数组时,使用两个临时数组
numsTmp
和indexTmp
分别存储排序后的元素和对应的下标。 - 使用两个指针
cur1
和cur2
分别指向左右子数组的起始位置,以及一个指针dest
指向临时数组的起始位置。 - 在合并过程中,比较
nums[cur1]
和nums[cur2]
的大小,并根据比较结果执行以下操作:- 如果
nums[cur1] <= nums[cur2]
,则将nums[cur1]
和index[cur1]
添加到临时数组中,并更新cur1
和dest
。同时,将ret[index[cur1]]
增加cur2 - mid - 1
(因为右子数组中当前位置之前的所有元素都比nums[cur1]
小)。 - 如果
nums[cur1] > nums[cur2]
,则直接将nums[cur2]
和index[cur2]
添加到临时数组中,并更新cur2
和dest
。
- 如果
- 合并完成后,将临时数组的内容复制回原数组
nums
和index
。
- 当合并两个已排序的子数组时,使用两个临时数组
- 返回结果:
- 在
countSmaller
函数中,调用mergeSort
函数对整个数组进行排序和逆序对计数。 - 返回存储逆序对数量的数组
ret
。
- 在
关键点
- 全局数组:使用全局数组
index
和ret
来存储下标和逆序对数量,确保在递归过程中能够正确地传递和更新这些信息。 - 归并排序变种:通过修改归并排序的合并过程,在合并两个有序子数组时同时统计逆序对的数量。
- 下标绑定:将元素与其在原始数组中的下标绑定在一起,以便在归并过程中能够正确地更新逆序对数量。
3.代码编写
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);
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, mid] [mid + 1, right]
// 2. 先处理左右两部分
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
// 3. 处理⼀左⼀右的情况
int cur1 = left, cur2 = mid + 1, i = 0;
while (cur1 <= mid && cur2 <= right) // 降序
{
if (nums[cur1] <= nums[cur2]) {
tmpNums[i] = nums[cur2];
tmpIndex[i++] = index[cur2++];
} else {
ret[index[cur1]] += right - cur2 + 1; // 重点,统计cur1后面有多少个元素比我小
tmpNums[i] = nums[cur1];
tmpIndex[i++] = index[cur1++];
}
}
//处理剩下的排序
while (cur1 <= mid) {
tmpNums[i] = nums[cur1];
tmpIndex[i++] = index[cur1++];
}
while (cur2 <= right) {
tmpNums[i] = nums[cur2];
tmpIndex[i++] = index[cur2++];
}
//还原
for (int j = left; j <= right; j++) {
nums[j] = tmpNums[j - left];
index[j] = tmpIndex[j - left];
}
}
};
The Last
嗯,就是这样啦,文章到这里就结束啦,真心感谢你花时间来读。
觉得有点收获的话,不妨给我点个赞吧!
如果发现文章有啥漏洞或错误的地方,欢迎私信我或者在评论里提醒一声~