题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。要求,时间复杂度O(nlogn),空间复杂度O(n)。
大家看到这个题目第一时间想到的是不是写一个嵌套循环直接遍历数组计算逆序对的数目,这种方法确实能够很简单的找到数组的逆序对数,但我们题目的要求是时间复杂度为O(nlogn),这种嵌套循环的时间复杂度却到了O(n^2),所以并不符合我们的要求,那么我们要用什么方法实现呢?很明显:归并排序。
首先我们来看一下归并排序实现求数组中逆序对的思路:
在归并排序的过程中,当合并两个有序的部分时,我们可以观察到以下现象:
- 如果左半部分的当前元素小于等于右半部分的当前元素,那么它们之间不存在逆序对。
- 如果左半部分的当前元素大于右半部分的当前元素,那么它们之间存在逆序对,并且逆序对的数量等于右半部分剩余元素的数量。
基于上述观察结果,我们可以在归并排序的过程中,通过统计逆序对的数量来完成逆序对的查找。
具体的实现思路如下:
- 将数组分割成两个部分,分别对左半部分和右半部分进行递归调用归并排序。
- 在合并两个有序部分的过程中,使用两个指针分别指向左半部分和右半部分的当前元素。
- 当合并过程中遇到左半部分的当前元素大于右半部分的当前元素时,统计逆序对的数量,并将逆序对的数量加到总的逆序对数量中。
- 将较小的当前元素放入合并后的有序数组中,并将对应指针向后移动一位。
- 重复步骤3和步骤4,直到其中一个部分的所有元素都合并完毕。
- 将剩余部分的元素依次放入合并后的有序数组中。
- 返回逆序对的数量。
代码实现如下:
#include <iostream>
#include <vector>
const int MOD = 1000000007;
// 归并函数:将两个有序数组合并为一个有序数组,并统计逆序对的数量
int merge(std::vector<int>& nums, int start, int mid, int end) {
std::vector<int> temp(end - start + 1); // 临时数组用于存储合并后的有序序列
int count = 0; // 统计逆序对的数量
int i = start; // 左序列指针
int j = mid + 1; // 右序列指针
int k = 0; // 临时数组指针
while (i <= mid && j <= end) {
if (nums[i] <= nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
count = (count + mid - i + 1) % MOD; // 统计逆序对数量
}
}
while (i <= mid) {
temp[k++] = nums[i++];
}
while (j <= end) {
temp[k++] = nums[j++];
}
for (i = start, k = 0; i <= end; i++, k++) {
nums[i] = temp[k]; // 将合并后的有序序列复制回原数组
}
return count;
}
// 归并排序函数:递归地对数组进行归并排序,并返回逆序对的数量
int mergeSort(std::vector<int>& nums, int start, int end) {
if (start >= end) {
return 0;
}
int mid = start + (end - start) / 2;
int count = 0;
count = (count + mergeSort(nums, start, mid)) % MOD; // 统计左半部分逆序对数量
count = (count + mergeSort(nums, mid + 1, end)) % MOD; // 统计右半部分逆序对数量
count = (count + merge(nums, start, mid, end)) % MOD; // 合并两部分并统计逆序对数量
return count;
}
// 统计逆序对的总数并取模
int countInversePairs(std::vector<int>& nums) {
int count = mergeSort(nums, 0, nums.size() - 1);
return count;
}
int main() {
std::vector<int> nums = {7, 5, 6, 4};
int count = countInversePairs(nums);
std::cout << "逆序对的数量为: " << count << std::endl;
return 0;
}