问题描述:
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
- 输入: [7,5,6,4]
- 输出: 5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof
注意该问题是逆序数对个数之和,如果是从小到大的数列,那么逆序数对总数为0,如果逆序数对是从大到小排列,那么逆序数对总数会是很高计算。例如【5,4,3,2,1】逆序数对总和为4+ 3 +2 + 1 =10。
问题求解使用到了归并排序,在排序整个数组的过程中将逆序的数对个数进行求解,使用递归进行归并排序,将整个数组分成两部分,分别用 i , j 指向这两个数组的起始位置,比较 nums[i]和nums[j]的大小 ,需要将小的那一个放入temp数组,如果nums[i]>nums[j],此时就意味着出现了逆序数对,注意理解的时候mid-i+1这个计算结果是在有序数组的基础上计算的,只有有序,数组中的元素才会放入到temp中,由于该过程涉及到递归,所以理解的时候要自下至上理解。如果数组有剩余部分没有遍历到,那么剩余的数组要拷贝到原始数组中。
结合该链接理解为什么是mid-i+1
归并排序模板
void merge(vector<int> nums,int l,int r)
{
if(l>=r)return;
int mid=l+r>>1;
merge(nums,l,mid); merge(nums,mid+1,r);
vector<int> tem;
int i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(nums[i]<=nums[j]) tem.push_back(nums[i++]);
else tem.push_back(nums[j++]);
}
while(i<=mid)tem.push_back(nums[i++]);
while(j<=r)tem.push_back(nums[j++]);
i=l;
for(auto t:tem) nums[i++]=t;//把tem排好序的数组放到原始数组里
}
算法 | 时间复杂度 | 空间复杂度 | 解释 |
---|---|---|---|
自底向上 | 数组实现O(nlogn) | O(n) | 不需要递归,需要额外的空间帮助合并 |
自底向上 | list实现O(nlogn) | O(1) | 不需要递归 |
自顶向上 | 数组实现O(nlogn) | O(n+logn) | 用到了数组,此外递归需要栈空间为logn |
自底向上 | list实现O(nlogn) | O(logn) | 递归需要栈空间为logn |
自底向上链表实现可以参考LeetCode 148 排序链表
class Solution {
public:
int merge(vector<int>&nums,int l,int r)
{
if(l>=r) return 0;
int mid=l+r >> 1;
int res=merge(nums,l,mid)+merge(nums,mid+1,r);
int i=l,j=mid+1;
vector<int> temp;
while(i<=mid&&j<=r)
{
if(nums[i]<=nums[j]) temp.push_back(nums[i++]);
else
{
temp.push_back(nums[j++]);
res+=mid-i+1;
}
}
while(i<=mid) temp.push_back(nums[i++]);
while(j<=r) temp.push_back(nums[j++]);
i=l;
for(auto x:temp) nums[i++]=x;
return res;
}
int inversePairs(vector<int>& nums) {
return merge(nums,0,nums.size()-1);
}
};