题目来源:65. 数组中的逆序对
Hard 题用暴力解法居然不超时…
代码:
class Solution
{
public:
int inversePairs(vector<int> &nums)
{
int n = nums.size();
int ans = 0;
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
if (nums[i] > nums[j])
ans++;
}
}
return ans;
}
};
复杂度分析:
时间复杂度:O(n2),其中 n 是数组 nums 的元素个数。
空间复杂度:O(1)。
O(n2) 的时间复杂度,即使在 AcWing 上能过,面试官也不会让你过的。
那么比暴力更快一点的复杂度为 O(nlogn),怎么才会有 logn 出现呢?
一半一半就会有 logn 出现。
怎么样把一半一半用到这道题上呢?
答案是归并排序。
归并排序详解:归并排序算法C++实现(超详细解析!!!!)。
举个例子:
将数组 [1,2,3,4,5,6,0] 对半切,变成 arr1=[1,2,3,4] 和 arr2=[5,6,0]。
将 arr1 里的 1 和 arr2 的 5 做比较,发现1<5,要是 arr2 里的 0 是 7 或者 8 就好了,这样 1 就比 arr2 里的所有数字都小,就不存在逆序对。
那么假设 arr2 排过序了,是[0,5,6],刚好 arr1 目前也是排过序的。
所以解法就是归并排序,只加了一行 countInversePairs += mid - i + 1
。
递归里面运行完两个 mergeSort 后,start ~ mid 和 mid+1 ~ end 都是有序的,且两个内部的逆序对都已经算过了,只需要算当前状态的逆序对就行了,不会重复。
代码:
class Solution
{
private:
int countInversePairs = 0;
public:
int inversePairs(vector<int> &nums)
{
if (nums.empty())
return 0;
mergeSort(nums, 0, nums.size() - 1);
return countInversePairs;
}
// 辅函数 - 归并排序
void mergeSort(vector<int> &nums, int left, int right)
{
if (left < right)
{
int mid = (left + right) / 2;
mergeSort(nums, left, mid); // 对 nums[left, mid] 进行排序
mergeSort(nums, mid + 1, right); // 对 nums[mid + 1, right] 进行排序
merge(nums, left, mid, right); // 合并
}
}
// 合并操作:将两个有序的子序列合并为一个有序序列
void merge(vector<int> &nums, int left, int mid, int right)
{
// 此时 nums[left, mid] 和 nums[mid+1, right] 都有序
vector<int> temp(right - left + 1); // 辅助数组
int i = left, j = mid + 1;
int k = 0; // k 为数组 temp 的下标
while (i <= mid && j <= right)
{
if (nums[i] <= nums[j])
temp[k++] = nums[i++];
else
{
temp[k++] = nums[j++];
// 因为 nums[left, mid] 是增序的,所以 nums[i, mid] 都大于 nums[j],共有 mid - i + 1 个逆序对
countInversePairs += mid - i + 1;
}
}
while (i <= mid)
temp[k++] = nums[i++];
while (j <= right)
temp[k++] = nums[j++];
// 将排好序的数组 temp 拷贝给数组 nums[left, right]
for (int i = left, k = 0; i <= right; i++, k++)
nums[i] = temp[k];
}
};
复杂度分析:
时间复杂度:O(n2),其中 n 是数组 nums 的元素个数。当有 n 个元素时,需进行 logn 轮归并排序,每一轮归并其比较次数不超过 n,元素移动次数都是 n。因此。归并排序的时间复杂度为 O(nlogn)。
空间复杂度:O(n),其中 n 是数组 nums 的元素个数。归并排序时需要和待排序区间内元素个数相等的存储空间。