这道题一上来最直观的做法就是将遍历一个数字,扫描之后的所有数字,只要遇到比自己小的那就将结果进行+1。这种写法非常简单,但是复杂度相当的高。时间复杂度O(n^2).
我们尝试用空间换时间。
我们利用分治的思想,也就是归并排序的思路去解析这道题。
首先我们把数组分解,一直分解到只有一个数字。那么单个数字和单个数字比大小,这样就很快得到了一个很小区间的逆序对的个数。
例如 图中的 11 和 8 , 7 和 1,既然已经统计了这两个数字的逆序对个数了,那么此后我们就不需要统计这两个的逆序对个数了。我们将它进行两两合并,排序处理。
两个数两个数的进行查看逆序对,
我们以 8 11 , 3 9 这两个序列为例。首先8 11的逆序对个数是1, 3 9 逆序对个数是0。
我们定义两个指针移动比较这两个数字。从后向前的去比较。定义第三个指针保存排序之后的元素序列。
public int reversePairs(int[] nums) {
if(nums.length == 0 || nums ==null){
return 0;
}
//用于保存操作后的数组
int copy[] = new int[nums.length];
//首先先分成单个数组
int count = InversePairs(nums,copy,0,nums.length-1);
return count;
}
//先分离再归并
public int InversePairs(int[] nums, int[] copy ,int start ,int end){
if(start == end){
copy[start] = nums[start];
return 0;
}
int length = (end - start)/2;
//左右区间先进行归并排序
int left = InversePairs(nums,copy,start,start+length);
int right = InversePairs(nums,copy,start+length+1,end);
//最后我们操作完nums数组,我们要将结果进行复制到copy数组,否则最后操作的还是没有
for (int i = start; i <= end; i++) {
copy[i] = nums[i];
}
//左区间最后一个下标
int i = start + length;
//右区间最后一个下标
int j = end;
//保存当前归并的逆序对
int count = 0;
//copy数组的指针
int copyIndex = end;
while(i >= start && j>= start+length+1){
if(copy[i] > copy[j]){
//如果剩余最后一个数字了,那么本身小于前面的数,还是要计数+1
count += j - (start+length);
nums[copyIndex--] = copy[i--];
}else{
//不构成逆序对,直接进入copy数组即可。
nums[copyIndex--] = copy[j--];
}
}
//如果有一方右剩余,需要继续copy
while(i >= start){
nums[copyIndex--] = copy[i--];
}
while(j >= start+length+1){
nums[copyIndex--] = copy[j--];
}
//最后的结果是左区间的逆序对+右区间的逆序对+整个区间的逆序对
return left + right + count ;
}