在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
很容易想到从后往前折半插入排序时顺便记录比当前数小得数,但是这种方法在用例30的时候就超时了。。
class Solution { public: int count(vector<int>& nums, int pivot, int st, int ed){ int l=st,r=ed; if(nums[pivot]<=nums[st])return 0; if(nums[pivot]>nums[ed]){ int tmp=nums[pivot]; for(int i=st;i<=ed;i++){ nums[i-1]=nums[i]; } nums[ed]=tmp; return ed-st+1; } while(l<r){ int mid=l+(r-l)/2; if(nums[mid]>=nums[pivot]) r=mid; else l=mid+1; } int tmp=nums[pivot]; for(int i=st;i<l;i++){ nums[i-1]=nums[i]; } nums[l-1]=tmp; return l-st; } int reversePairs(vector<int>& nums) { //从后往前折半插入排序 int n=nums.size(); int res=0; for(int i=n-2;i>=0;i--){ res+=count(nums,i,i+1,n-1); } return res; } };
然后我就思考毕竟插入排序要移动数组元素,最后的总体时间复杂度是O(n^2),那么找到一个插入和比较都是logn复杂度的算法不就可以了吗,那不就是归并排序吗!
当然,需要O(n)的额外空间,时间复杂度为O(logn)
class Solution { public: int res=0; void merge(vector<int>& nums, int left, int mid, int right){ vector<int> tmp(right-left+1,0); int l1=left,l2=mid+1,l3=0; while(l1<=mid&&l2<=right){ if(nums[l1]<=nums[l2]){ tmp[l3++]=nums[l1++]; res+=(l2-mid-1); } else tmp[l3++]=nums[l2++]; } while(l1<=mid){ tmp[l3++]=nums[l1++]; res+=(l2-mid-1); } while(l2<=right)tmp[l3++]=nums[l2++]; for(int i=left;i<=right;i++){ nums[i]=tmp[i-left]; } } void mergeSort(vector<int>& nums, int left, int right){ if(left<right){ int mid = left+(right-left)/2; mergeSort(nums,left,mid); mergeSort(nums,mid+1,right); merge(nums,left,mid,right); } } int reversePairs(vector<int>& nums) { //归并排序 int n=nums.size(); mergeSort(nums,0,n-1); return res; } };