[路飞]_LeetCode_剑指 Offer 51. 数组中的逆序对

147 阅读1分钟

「这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战

题目

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

示例 1:

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

限制:

0 <= 数组长度 <= 50000

来源:力扣(LeetCode)leetcode-cn.com/problems/sh…

解题思路

二路归并排序当中的合并阶段,需要比对左右两个数组第一个元素的大小,当右边数组第一个元素小于左边数组第一个元素时,左边数组的个数就是能和右边数组第一个元素组成逆序对的数量。因此我们可以利用归并排序这个特性统计逆序对。

代码实现

var reversePairs = function(nums) {
    //对数组 nums 的第 0 到最后一位进行排序
    return countResult(nums, 0, nums.length - 1)
};

//将待排序数组 nums 的第 l 到 r 区间进行排序,并返回该区间中的逆序对
var countResult = function (nums, l, r) {
    //如果l大于等于r时,区间只有一个元素或没有元素,不需要排序,逆序对为0
    if (l >= r) return 0

    //获取中间位置,等于 l + r 除 2
    const mid = (l + r) >> 1
    let ans = 0

    //对 nums 的 l 到 mid 区间排序,并计算逆序对
    ans += mergeSort(nums, l, mid)
    //对 nums 的 mid + 1 到 r 区间排序,并计算逆序对
    ans += mergeSort(nums, mid + 1, r)

    //arr 存放排序结果
    const arr = []
    //定义双指针,p1 从l开始到mid,p2 从mid+1开始到r
    let p1 = l
    let p2 = mid + 1

    while (p1 <= mid || p2 <= r) {
        //如果右边的数组已经为空,或者左边第一个元素小于右边数组的第一个元素时
        //把左边的元素加入结果中,并且左边指针后移
        //否则把右边的第一个元素加入结果中,并且右边指针后移
        if (p2 > r || (p1 <= mid && nums[p1] < nums[p2])) {
            arr.push(nums[p1++])
        } else {
            arr.push(nums[p2++])
            
            //左边数组剩余元素都大于右边数组第一个元素,能组成逆序个数为左边元素个数
            ans += mid - p1 + 1
        }
    }

    //将排序结果复制到原数组
    for (let i = l; i <= r; i++) nums[i] = arr[i - l]
    
    //返回逆序对
    return ans;
}

如有错误欢迎指出,欢迎一起讨论!