题解 |【清晰图解】 #数组中的逆序对#"分而治之"妙啊

已默认你已读懂题意了哈

alt

我的解题思路如下

其实我觉得归并排序是跟逆序对是息息相关的,因为归并本质体现的是一种 “分而治之” 的思想

那问题来了?

  • 怎么分: 不断从数组的中点位置划开(即二分法),然后把整个数组的排序问题转化成一个子数组的排序问题;

  • 怎么治: 划分到子数组它的长度为 1 时,开始向上合并,不断把 较短排序数组 合并成 较长排序数组,这样直到合并成原数组就完成排序了啊;

举个例

下面是数组 [7,3,2,6,0,1,5,4] 的归并排序

看上面的图是合并阶段

本质上是合并两个已经排好序的数组的过程,而每当遇到 左子数组的当前元素 > 右子数组的当前元素 的时候,意味着左边子数组的当前元素 到 末尾元素 和 右边子数组的当前元素构成了若干的逆序对;

我们把左边子数组 [2, 3, 6, 7]和右边子数组 [0, 1, 4, 5] 的合并与逆序来演示下

所以,考虑在归并排序的合并阶段统计「逆序对」数量,完成归并排序的时候,也随之完成所有逆序对的统计。

算法描述

  • 终止条件是啥: 当 l≥r 时,代表着子数组长度是 1 ,此时必须终止划分;
  • 递归划分: 先算数组中点m在哪里, 然后递归划分左边子数组 merge_sort(l, m) 以及右边子数组 merge_sort(m + 1, r)

合并与逆序思路如下

先把数组nums的闭区间[i,r]内的元素至移动到辅助数组tmp里面; 然后设置双指针 i, j分别指向左/右子数组的第一个元素;

  • 当i=m+1时:代表的是左边子数组已合并完了,所以添加右边子数组当前元素tmp[j],并执行 j=j+1;
  • 否则,当j=r+1时:代表的是右子数组已经合并完了,所以添加左子数组当前元素tmp[i],并执行i=i+1;
  • 否则,当tmp[i]≤tmp[j]时:添加左边子数组的当前元素即tmp[i] ,并执行i=i+1;
  • 否则(即tmp[i]>tmp[j])时: 添加右子数组当前元素即tmp[j] ,并执行j=j+1;此时构成m−i+1个「逆序对」,统计添加至resres 也就是返回值,代表的是目前返回的逆序对总数 resres;

为了简化代码,j=r+1 时 和 当 tmp[i]≤tmp[j] 的时侯,两判断表达式可以合并。

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

主方法完成两件事

  • 对辅助数组 tmp 进行初始化,用于合并阶段暂存元素;
  • 然后执行归并排序方法 merge_sort(),并返回逆序对总数即可;

举个例子

下图,为数组[7, 3, 2, 6, 0, 1, 5, 4]的归并与逆序排序如下所示

复杂度分析下

  • 时间复杂度是 O(NlogN): 其中 N 为数组长度;归并排序使用 O(NlogN) 时间;
  • 空间复杂度是 O(N):因为辅助数组 tmp 占用 O(N) 大小的额外空间;
全部评论

相关推荐

05-21 18:32
已编辑
湖南工学院 Java
这条干货多数是给i人朋友们分享的,知道你们开不了口,可以试试我说的这些方法1.调整心态:接受初期的尴尬刚开始进入一个新环境,双方都属于一个认识对方的过程,尴尬瞬间是难免存在的。首先,你要接受尴尬,允许自己犯错,实习期本身就是来学习的,同事也不会期待你完美无缺。另外,不要太以自我为中心,其实你的尴尬瞬间也许没有人在意,是因你的对自己的关注而放大了不安全感。2.准备一些防止尴尬的话题和工作相关的,可以以请教的方式开启。比如:xx,这个表格我没有看懂,可以给我讲一下吗非工作的话题,可以聊聊中午吃什么、当地的天气如何、通勤远不远之类的。比如:附近有什么好吃的外卖吗?我刚来还不太熟悉3.每日练习,逐渐打...
sweep^0416:内向人,遇到好的领导很重要,我之前一段实习组里全e人就我一个i 刚入职第一周还会带着我聊一下,后面越来越冷落我,实在受不了,每天去到就想亖,mentor还要pua说是我融入不了集体(我真的以为是我的问题)后面我离职了,去了现在这一家公司,我的领导也是e人,但是我融入的很好,组里的人全都很好很好,也不会出现小团体什么的,所以说内向不是不融入环境的根本,就是公司跟带教的问题
点赞 评论 收藏
分享
04-08 13:31
已编辑
门头沟学院 前端工程师
D0cC:京东营收1万多亿人民币,阿里9000多亿,虽然他俩利润都没腾讯和字节多,但是很恐怖了啊,负担了多少打工人的薪水
投递拼多多集团-PDD等公司10个岗位
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务