题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
解法1:暴力求解
如果需要求出数组中的逆序对的总数,需要对数组进行双重遍历,找出数组中所有的逆序对。代码如下:
class Solution {
public:
int reversePairs(vector<int>& nums) {
int result = 0;
for (size_t i = 0; i < nums.size(); ++i) {
for (size_t j = i + 1; j < nums.size(); ++j) {
if (nums[i] > nums[j]) {
++result;
}
}
}
return result;
}
};
由于时间复杂度为O(n^2)
,因此执行结果超出时间限制。
解法2:归并排序
由于解法1的时间复杂度为O(n^2)
,那么能不能想办法将时间复杂度降低为O(nlogn)
呢?此时可以采用归并排序进行处理,在进行归并处理时,可以求出某一个区间内的逆序对个数,并且针对该区间进行排序后不影响整体的逆序对求解。当最终完成排序后,可求出整个区间的逆序对个数。
代码如下:
class Solution {
public:
int reversePairs(vector<int>& nums)
{
buffer_.resize(nums.size());
sort(nums, 0, static_cast<std::ptrdiff_t>(nums.size()) - 1);
return pairs;
}
private:
void sort(vector<int>& nums, ptrdiff_t left, ptrdiff_t right)
{
if (left >= right) {
return;
}
ptrdiff_t mid = left + (right - left) / 2;
sort(nums, left, mid);
sort(nums, mid + 1, right);
if (nums[mid] > nums[mid + 1]) {
merge(nums, left, mid, right);
}
}
void merge(vector<int>& nums, ptrdiff_t left, ptrdiff_t mid, ptrdiff_t right)
{
copy(nums.begin() + left, nums.begin() + right + 1, buffer_.begin() + left);
ptrdiff_t i = left;
ptrdiff_t j = mid + 1;
for (ptrdiff_t index = left; index <= right; ++index) {
if (i > mid) { // 左区间已经遍历完,只需处理右区间数据即可
nums[index] = buffer_[j];
++j;
} else if (j > right) { // 右区间已经遍历完,只需处理左区间数据即可
nums[index] = buffer_[i];
++i;
} else if (buffer_[i] > buffer_[j]) {
// 若左区间中buffer_[i] > buffer_[j], 那么buffer_中[i, mid]的值均大于buffer_[j], 逆序对个数为(mid - i + 1)
pairs += (mid - i + 1);
nums[index] = buffer_[j];
++j;
} else {
nums[index] = buffer_[i];
++i;
}
}
}
private:
std::vector<int> buffer_;
int pairs = 0;
};
时间复杂度降低为O(nlogn)
,空间复杂度为O(n)
,执行结果如下: