【剑指Offer-51】数组中的逆序对
问题
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例
输入: [7,5,6,4]
输出: 5
解答
class Solution {
public:
int reversePairs(vector<int>& nums) {
tmp.resize(nums.size());
mergeSort(nums, 0, (int)nums.size() - 1);
return res;
}
private:
vector<int> tmp;
int res = 0;
void mergeSort(vector<int>& nums, int left, int right) {
if (left >= right) return;
int mid = left + (right - left) / 2;
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
merge(nums, left, mid, right);
}
void merge(vector<int>& nums, int left, int mid, int right) {
int i = left, j = mid + 1, cnt = left;;
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) tmp[cnt++] = nums[i++];
else {
res += mid - i + 1; // 与归并排序的区别
tmp[cnt++] = nums[j++];
}
}
while (i <= mid) tmp[cnt++] = nums[i++];
while (j <= right) tmp[cnt++] = nums[j++];
for (int i = left; i <=right; i++) nums[i] = tmp[i];
}
};
重点思路
归并排序的详解参考【C++】排序算法。归并排序非常适合处理前后数字对之间大小关系的问题。对于两个有序数列,其中某一个数字满足要求时,该数列后续所有数都满足要求。本题更一般的写法可以将上述代码20~26行改为以下内容:
while (i <= mid && j <= right) { // 排序
if (nums[i] <= nums[j]) tmp[cnt++] = nums[i++];
else tmp[cnt++] = nums[j++];
}
int ii = left, jj = mid + 1;
while (ii <= mid && jj <= right) { // 题目要求
if (nums[ii] <= nums[jj]) ii++;
else {
res += mid - ii + 1;
jj++;
}
}
这个写法先完成两个数组的排序操作,再完成本题的要求。但是很容易发现下面的while循环和上面的while循环的内外部条件语句都是相同的,所以可以合并,就得到了最上面的那份代码。
这个更一般的写法可以使用在【LeetCode-493】翻转对。