归并排序思路+模板+例题

1 思路

归并排序采用了经典的分治策略,通过递归求解。
要解决的问题就分为“分”和“治”两个步骤。
分的阶段可以看做递归拆分子序列的过程,递归深度为log2N。
治的阶段也就是归并的阶段,将拆分开的子序列,相邻的两两合并。
时间复杂度 O(nlogn)

2 模板

1.分治合并

    public static void mergeSort(int nums[], int first, int last, int temp[])
    {
        if (first < last){
            int mid = (first + last) / 2;
            mergeSort(nums, first, mid, temp);    //左边有序
            mergeSort(nums, mid + 1, last, temp); //右边有序
            mergeArray(nums, first, mid, last, temp); //再将二个有序数列合并
        }
    }

2.合并部分代码

    public static void mergeArray(int nums[], int first, int mid, int last, int temp[])
    {
        //i是左边有序数组索引,j是右边有序数组索引,k是temp数组索引,
        //为了便于理解保持temp和nums的索引范围相同
        int i = first, j = mid + 1,k=first;
        /**
         * 下面这段就是合并两个有序数组的代码
         */
        while (i <= mid && j <= last) {
            if (nums[i] <= nums[j])
                temp[k++] = nums[i++];
            else
                temp[k++] = nums[j++];
        }
        while (i <= mid)
            temp[k++] = nums[i++];
        while (j <= last)
            temp[k++] = nums[j++];
        //temp数组是nums[first]到nums[last]这段的有序排列,对应索引赋值给nums
        for (i = first; i < k; i++)
            nums[i] = temp[i];
    }

3.测试代码

    public static void main(String[] args) {
        int []nums={5,7,8,9,6,4,7,5,5};
        //为了不在合并过程中反复创建temp数组,提前创建好与nums同样大小的数组不断重用。
        int []temp =new int[nums.length];
        mergeSort(nums,0,nums.length-1,temp);
        for(int num:nums){
            System.out.println(num);
        }
    }

3 例题——剑指offer(数组中的逆序对)

题目

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例

输入: [7,5,6,4]
输出: 5

思路

在归并排序的过程中进行逆序对的计数。只需要再归并排序的框架中增加计数的代码。

代码

class Solution {
    public int reversePairs(int[] nums) {
        int[] temp = new int[nums.length];
        return mergeSort(nums, 0, nums.length-1, temp);
    }
    public int mergeSort(int[] nums, int first, int last, int[] temp) {
        if (first < last){
            int mid = (first + last) / 2;
            //左边有序并计数
            int countLeft = mergeSort(nums, first, mid, temp);
            //右边有序并计数
            int countRight=mergeSort(nums, mid + 1, last, temp); 
            //再将二个有序数列合并并计数
            int count =mergeArray(nums, first, mid, last, temp); 
            return count+countLeft+countRight; 
        }
        else
            return 0;
    }
     public static int mergeArray(int nums[], int first, int mid, int last, int temp[]){
        int i = first, j = mid + 1,k=first,count=0;
        while (i <= mid && j <= last) {
            if (nums[i] <= nums[j])
                temp[k++] = nums[i++];
            else{
                temp[k++] = nums[j++];
                //除了归并过程之外,加上归并过程中的逆序对计数
                //每当右半数组有元素小于左数组时,左数组中当前的个数就是要增加的逆序对的个数
                count=count+mid-i+1;
            }
        }
        while (i <= mid)
            temp[k++] = nums[i++];
        while (j <= last)
            temp[k++] = nums[j++];
        for (i = first; i < k; i++)
            nums[i] = temp[i]; 
        return count;
    }
}

猜你喜欢

转载自blog.csdn.net/tianyouououou/article/details/105023510