剑指offer 51 - 数组中的逆序对
1 题目
https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof
2 题意
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
3 思路
author's blog == http://www.cnblogs.com/toulanboy/
3.1 基础想法
(1)本题要求的是逆序对,逆序对的定义题面已经给出。
(2)而求逆序对的常见解法是借助归并排序来实现,更具体是借助他的merge过程。
3.2 归并排序求逆序对
(1)归并逻辑
我们知道,归并排序实际上分治的思想,归并排序的逻辑如下:
- 其先将数组不断细分,最后细分到每个数组只有
1个元素
,那么这个只包含1个元素
数组中就是有序的。 - 然后将两个有序的长度为1的数组依次合并为长度为2的有序数组,后面合并过程就是依次类推,
2合4
,4合8
,...,n/2合n
。
(2)Merge过程
值得注意的是:在上述的合并过程中,我们是可以得到其逆序对个数的?
举个例子来说明合并过程:
数组A: 11 22 33 44
数组B: 12 20 55 66
数组A和数组B合并为一个数组。
正常做法是:分别用2个指针p1, p2指向A和B的第一个元素。然后进行两两对比,谁小谁下来放进新数组。
进一步地,我们可以利用这个合并过程。若在上述例子中,p1指向22, p2指向20。那么可知p1 > p2,,们可知逆序对(22, 20),而且还可知道p1后面的数,都比20要大,所以可得到逆序对(33, 20)、(44, 20)。 故可得到的逆序对个数是p1后面剩余的元素个数。
通过合并过程累加,就可以得到整个序列的逆序对。
可能有同学想问:上述合并,只是得到了两个相对位置数组的逆序对,但逆序对个数还应该包括这个位置相对其他位置的逆序对,这部分是如何统计上去的?
答:实际上,上述两个相对位置数组合并为1个大数组后,他必然会经历和前面大数组、后面大数组的过程,所以他前面的元素、他后面的元素相对他的逆序对个数也会被统计到的。
4 代码
//author's blog == http://www.cnblogs.com/toulanboy/
class Solution {
public:
int ans = 0;
vector<int> temp;//辅助数组,用来存储新的有序数组
void fenzhi(vector<int>& nums, int left, int right){
if(left >= right){
return;
}
int mid = (left+right) >> 1;
fenzhi(nums, left, mid);//拆分,得到左边
fenzhi(nums, mid+1, right);//拆分,得到右边
merge(nums, left, mid, right);//左右合并
}
void merge(vector<int>& nums, int left, int mid, int right){
int p1 = left;
int p2 = mid+1;
temp.clear();
while(p1 <= mid && p2 <= right){
if(nums[p1] <= nums[p2]){
temp.push_back(nums[p1]);
p1++;
}
else{
temp.push_back(nums[p2]);
p2++;
ans += (mid-p1+1);
}
}
while(p1 <= mid){
temp.push_back(nums[p1]);
p1++;
}
while(p2 <= right){
temp.push_back(nums[p2]);
p2++;
}
for(int i=0; i<temp.size(); ++i){
nums[left+i] = temp[i];
}
}
int reversePairs(vector<int>& nums) {
fenzhi(nums, 0, nums.size()-1);
return ans;
}
};
❤ 如果这篇文章对你有帮助,麻烦来个大拇指👍,谢谢。