题目来自数组中的逆序对
采用模拟归并排序的过程统计逆序对的数目;
public class Solution {
private long sum; //统计逆序对的个数
private int[] nums;
//归并排序
public int reversePairs(int[] nums) {
sum=0;
this.nums=nums;
int l=0;
int r=nums.length-1;
divide(l,r);
return (int)sum;
}
/**
* 待分解的区间[l,r]
* @param l
* @param r
*/
private void divide(int l,int r){
System.out.println("divide("+l+","+r+")");
System.out.println();
if (l==r)
return;
int mid=(l+r)>>1; //取中间值
divide(l,mid); //对左边[l,mid]进行递归
divide(mid+1,r); //对右边[mid+1,r]进行递归
merge(l,r,mid);
}
private void merge(int l, int r, int mid) {
System.out.println("merge(l:"+l+",r:"+r+",mid:"+mid+")");
System.out.print("当前nums数组为:");
for(int n=0;n<nums.length;n++)
System.out.print(nums[n]+" ");
System.out.println();
System.out.print("当前左区间为:");
for(int p=l;p<=mid;p++)
System.out.print(nums[p]+",");
System.out.print(" ");
System.out.print("当前右区间为:");
for(int p=mid+1;p<=r;p++)
System.out.print(nums[p]+",");
System.out.println();
int i=l; //左区间的起点
int j=mid+1; //右区间的起点
int[] tmp=new int[r-l+1]; //排序的数组
int index=0;
// System.out.println("l="+l);
// System.out.println("r="+r);
while (i<=mid && j<=r){
if(nums[i]>nums[j]){
System.out.println("前大于后,构成逆序,需要排序:"+nums[i]+">"+nums[j]);
System.out.println(nums[j]+" 已加到tmp数组");
tmp[index++]=nums[j++]; //num[i]留到后面加到index数组
sum += mid-i+1; //此前,[l,mid]已经保证是有序(升序)的了,所以num[i]后面的都是大于num[j]的,都与num[j]构成逆序对
}
else {
System.out.println("前小于后,不需要排序:"+nums[i]+"<"+nums[j]);
System.out.println(nums[i]+" 已加到tmp数组");
tmp[index++]=nums[i++];
}
}
//上面while循环的两个条件可能有一个还满足
while (i<=mid){
System.out.println(nums[i]+" 已加到tmp数组");
tmp[index++]=nums[i++];
}
while (j<=r) {
System.out.println(nums[j]+" 已加到tmp数组");
tmp[index++] = nums[j++];
}
index=0;
System.out.print("[nums[l:"+l+"], nums[r:"+r+"]]左右区间合并后: ");
for(int k=l; k<=r; k++){
nums[k]=tmp[index++]; //使用临时排序数组更新原数组
System.out.print(nums[k]+" ");
}
System.out.println("\n-----------");
}
public static void main(String[] args) {
int[] nums=new int[]{3,2,6,7,3,0,1};
new Solution().reversePairs(nums);
}
}
其递归的执行过程,自己大致画了以下:
其次,上述代码的执行输出如下:
divide(0,6)
divide(0,3)
divide(0,1)
divide(0,0)
divide(1,1)
merge(l:0,r:1,mid:0)
当前nums数组为:3 2 6 7 3 0 1
当前左区间为:3, 当前右区间为:2,
前大于后,构成逆序,需要排序:3>2
2 已加到tmp数组
3 已加到tmp数组
[nums[l:0], nums[r:1]]左右区间合并后: 2 3
------------------------
divide(2,3)
divide(2,2)
divide(3,3)
merge(l:2,r:3,mid:2)
当前nums数组为:2 3 6 7 3 0 1
当前左区间为:6, 当前右区间为:7,
前小于后,不需要排序:6<7
6 已加到tmp数组
7 已加到tmp数组
[nums[l:2], nums[r:3]]左右区间合并后: 6 7
------------------------
merge(l:0,r:3,mid:1)
当前nums数组为:2 3 6 7 3 0 1
当前左区间为:2,3, 当前右区间为:6,7,
前小于后,不需要排序:2<6
2 已加到tmp数组
前小于后,不需要排序:3<6
3 已加到tmp数组
6 已加到tmp数组
7 已加到tmp数组
[nums[l:0], nums[r:3]]左右区间合并后: 2 3 6 7
------------------------
divide(4,6)
divide(4,5)
divide(4,4)
divide(5,5)
merge(l:4,r:5,mid:4)
当前nums数组为:2 3 6 7 3 0 1
当前左区间为:3, 当前右区间为:0,
前大于后,构成逆序,需要排序:3>0
0 已加到tmp数组
3 已加到tmp数组
[nums[l:4], nums[r:5]]左右区间合并后: 0 3
------------------------
divide(6,6)
merge(l:4,r:6,mid:5)
当前nums数组为:2 3 6 7 0 3 1
当前左区间为:0,3, 当前右区间为:1,
前小于后,不需要排序:0<1
0 已加到tmp数组
前大于后,构成逆序,需要排序:3>1
1 已加到tmp数组
3 已加到tmp数组
[nums[l:4], nums[r:6]]左右区间合并后: 0 1 3
------------------------
merge(l:0,r:6,mid:3)
当前nums数组为:2 3 6 7 0 1 3
当前左区间为:2,3,6,7, 当前右区间为:0,1,3,
前大于后,构成逆序,需要排序:2>0
0 已加到tmp数组
前大于后,构成逆序,需要排序:2>1
1 已加到tmp数组
前小于后,不需要排序:2<3
2 已加到tmp数组
前小于后,不需要排序:3<3
3 已加到tmp数组
前大于后,构成逆序,需要排序:6>3
3 已加到tmp数组
6 已加到tmp数组
7 已加到tmp数组
[nums[l:0], nums[r:6]]左右区间合并后: 0 1 2 3 3 6 7
------------------------
以上的执行结果与调用树描述一致,读者可以亲自上手执行代码,结合相关输出可以更加深入的理解归并排序的写法和执行细节。