📟作者主页:慢热的陕西人
🌴专栏链接:力扣刷题日记
📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言
文章目录
- 牛客热题:逆序对数量
- 题目链接
- 方法一:归并排序
- 思路
- 代码
- 复杂度
牛客热题:逆序对数量
题目链接
数组中的逆序对_牛客题霸_牛客网 (nowcoder.com)
方法一:归并排序
思路
-
逆序对:数组左边边的元素大于右边的元素则被称为一个逆序对
-
首先归并排序的思路就是将两个原本有序的数组合并为一个数组
-
归并排序内部又是将原数组分成很多个小区间,然后相邻的小区间再进行合并
-
那么在合并的途中,就涉及到那些元素的位置需要交换。
-
需要交换的位置就是一个逆序对,但是因为待合并的数组都是有序的,所以其之后的每一个元素也都会产生逆序对 即:
a[i] > a[j],则a[i] 和它后面的元素都大于 a[j]
(其中i,j是两个待合并数组的下标)
代码
// 合并两个有序数组
int res = 0;
void merge(vector<int>& arr, vector<int>& temp, int l, int m, int r) {
int i = l, j = m + 1, k = 0;
while (i <= m && j <= r) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
// 奥妙之处
//若 a[i] > a[j],则a[i] 和它后面的元素都大于 a[j]
res += (m - i + 1);
res %= 1000000007;
}
}
while (i <= m) {
temp[k++] = arr[i++];
}
while (j <= r) {
temp[k++] = arr[j++];
}
// 将临时数组中的数据复制回原数组
for (int p = 0; p < k; ++p) {
arr[l + p] = temp[p];
}
}
// 归并排序函数
void mergeSort(vector<int>& arr, vector<int>& temp, int l, int r) {
if (l < r) {
// 计算中间点
int m = l + (r - l) / 2;
// 递归地对左半部分和右半部分进行排序
mergeSort(arr, temp, l, m);
mergeSort(arr, temp, m + 1, r);
// 合并已排序的两部分
merge(arr, temp,l, m, r);
}
}
int InversePairs(vector<int>& nums)
{
//在外部开辟好辅助数组的空间
//函数内部开辟的话,因为是递归式的调用,所以要多次构造和析构辅助数组
//效率较低
vector<int> temp(nums.size());
mergeSort(nums,temp, 0, nums.size() - 1);
return res;
}
复杂度
时间复杂度:O( N l o g N NlogN NlogN),采用了归并排序的思路
空间复杂度:O(N), 借用了和原数组长度相同的辅助数组