归并排序递归(debug版理解)

题目来自数组中的逆序对
采用模拟归并排序的过程统计逆序对的数目;

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 
------------------------

以上的执行结果与调用树描述一致,读者可以亲自上手执行代码,结合相关输出可以更加深入的理解归并排序的写法和执行细节。

你可能感兴趣的:(算法)