分治--归并排序

剑指 Offer 51. 数组中的逆序对
首先来回忆一下归并排序
在这里插入图片描述
利用递归,首先递归划分子数组,然后再递归合并
递归函数参数为子数组的左右两个下标,记为l,r
递归终点就是l>=r,即子数组只有一个元素,此时不用再划分了,可以直接返回进行合并

合并的过程,先将子数组赋值给辅助数组,然后定义两个指针,分别指向两个子数组(子数组已经有序),通过判断条件来移动指针,修改原数组(合并)

下面是力扣中这道题的题解,基本就是归并排序模板,多加了一行计算逆序对的代码。

class Solution {
    int[] nums, tmp;
    public int reversePairs(int[] nums) {
        this.nums = nums;
        tmp = new int[nums.length];
        return mergeSort(0, nums.length - 1);
    }
    private int mergeSort(int l, int r) {
        // 终止条件
        if (l >= r) return 0;
        // 递归划分
        int m = (l + r) / 2;
        int res = mergeSort(l, m) + mergeSort(m + 1, r);
        // 合并阶段
        int i = l, j = m + 1;
        //先将子数组赋值给辅助数组tmp,方便之后直接修改原数组
        for (int k = l; k <= r; k++)
            tmp[k] = nums[k];
        for (int k = l; k <= r; k++) {
            //已经遍历完左子数组,直接加入右子数组元素,对应指针向后移动一位
            if (i == m + 1)  
                nums[k] = tmp[j++];
            //已经遍历完右子数组,或者左子数组元素小于等于右子数组元素,加入左子数组元素,对应指针向后移动一位
            else if (j == r + 1 || tmp[i] <= tmp[j])
                nums[k] = tmp[i++];
            else {  //最后一种情况,左子数组元素大于右子数组元素,由于数组已经有序,那么左子数组后面所有元素一定都大于该右子数组元素
                nums[k] = tmp[j++];
                res += m - i + 1; // 统计逆序对
            }
        }
        return res;
    }
}

再来看一下归并排序的时间复杂度和空间复杂度

时间复杂度:归并排序总时间=分解时间+子序列排好序时间+合并时间。
无论每个序列有多少数都是折中分解,所以分解时间是个常数,可以忽略不计。
因此,归并排序总时间=子序列排好序时间+合并时间

假设一个序列有n个数的排序时间为T(n),T(n)是一个关于n的函数,随着n的变化而变化。
那么我们将n个数的序列,分为两个(n/2)的序列。
在这里插入图片描述
那么T(n)=2*T(n/2)+合并时间

由于合并时,两个子序列已经组内排好序了,看归并排序的代码也可以看出,指针最多只遍历子数组一次,因此时间复杂度为n

所以T(n)=2*T(n/2)+n

以此类推
在这里插入图片描述
上图类似一个二叉树,到最后一层时,每一层n的系数就是二叉树的层数
可知,T(n)=n*T(1)+(logn)*n,T(1)为一个常数,所以时间复杂度为O(nlogn)
此处参考这篇博客

空间复杂度
需要使用一个长度为n的辅助数组,所以空间复杂度为O(n)

**最后的疑问:**为什么计算逆序对会和归并排序联系在一起?
但好像就是根据经验,利用「归并排序」计算逆序对,是非常经典的做法。

什么是分治思想?
分治思想就是把复杂问题、拆分成诺干个相同的小问题,然后将问题逐步解决掉,合并到一起的过程,就是分治思想。简单来说,分治思想就是“分而治之”,将复杂问题拆分成诺干个相同的小问题进行解决。

三个步骤:

1.分解成很多子问题

2.解决这些子问题

3.将解决的子问题合并从而解决整个大问题

归并排序,二分查找等等递归问题都是使用了分治的思想

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值