什么是逆序对:
对于一个包含n个整数的数组arr[1…n],如果有i < j,且arr[ i ]>arr[ j ],则称(A[ i] ,A[ j] )为数组arr中的一个逆序对。
一般思维:
蛮力枚举:
即两层for循环遍历每个元素,这样的算法时间复杂度为O(n2).
那么我们能否利用分治法去寻找一个更有效的方法去解决问题呢。
分治法解决逆序对:
我们可以参考归并排序的过程,结合归并排序每次比较排序之后的有序性,在合并的过程中进行统计逆序对的个数,(不太了解分治法和归并排序的读者,可以点击查看我的另一篇博文:归并排序(分而治之法))。
合并之后,合并在一起的两个部分之间都已经统计出了这两个部分的逆序对,之后就不需要再对合并后的自身进行统计啦,而且自身已经是有序的了(从小到大),只需要在对自身与要与自己合并的部分进行统计。
代码部分只需对归并排序的合并过程统计逆序对数,其他步骤和归并排序都一样。
java完整代码(递归实现):
class Solution {
public int reversePairs(int[] nums) {
return reversePairs1(nums,0,nums.length-1,nums.clone()); // 归并排序的思路
}
public int reversePairs1(int[] nums,int left,int right,int[] tempArr){
if(left>=right){
return 0;
}
int mid = (right+left)/2;
int leftRever = reversePairs1(nums,left,mid,tempArr);
int rightRever = reversePairs1(nums,mid+1,right,tempArr);
int mergerRever = mergerPairs(nums,left,mid,right,tempArr);
return leftRever+rightRever+mergerRever;
}
public int mergerPairs(int[] arr,int left,int mid,int right,int[] tempArr){
int i = left,j = mid+1, numberRever = 0, tempIndex = left;
while(i<=mid && j<=right){
if(tempArr[i]>tempArr[j]){
numberRever += mid - i + 1; // 因为左右两边已经从小到大排好了,所以可以任务当前指针后边的都比当前值大 ,如果左边的当前值是大于右边的当前值,那么左边后面的也一定都大于右边的当前值
arr[tempIndex++] = tempArr[j++]; // 归并排序的思路
}else{
arr[tempIndex++] = tempArr[i++]; // 归并排序的思路
}
}
while(i <= mid){
arr[tempIndex++] = tempArr[i++];
}
while(j<=right){
arr[tempIndex++] = tempArr[j++];
}
//需要把tempArr数组变为该轮排序后的数组,arr已经两个两个结合之后已经按照从小到大的顺序排好序了,这里只需要赋值给tempArr即可。
for (i = left;i<=right;i++){
tempArr[i] = arr[i];
}
return numberRever;
}
}
欢迎讨论!