[路飞]_leetcode-剑指 Offer 51-数组中的逆序对

406 阅读1分钟

题目描述

[题目地址]

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

示例 1:

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

限制:

0 <= 数组长度 <= 50000

解题思路

本题最简单的一个解题思路就是直接一个双层循环,然后保证内层元素的下标大于外层元素,如果外层元素大于内层元素,则组成一个逆序对,记录数量。双层循环完成,就得到了所有的逆序对。
这样的一个解题思路没有问题,但是提交后会发现超出时间限制,也是就 O(n²) 的时间复杂度是不满足本题要求的。

那还有什么更高效的方式可以解题呢?
这里我们可以利用归并排序解决本题。因为归并排序的回溯过程中,会进行左右数组的组合,当此时选择到的是右侧数组中的元素的时候,如果左侧数组中有未处理的元素,因为前后位置上,左侧数组的元素都在右侧数组元素之前,又因为此时左侧未处理元素值都比当前元素值大,所以此时左侧数组中每一个未处理的元素都与当前元素组成一个逆序对,记录这样的逆序对的数量,当归并排序回溯完成,就得到了数组中所有逆序对的数量。

动画演示

leetcode-剑指 Offer 51-数组中的逆序对.gif

代码实现

 let temp = [];
var reversePairs = function(nums) {
    while(temp.length < nums.length) temp.push(0);
    return countReversePairs(nums,0,nums.length-1)
};

var countReversePairs = function(nums,leftRoot,rightRoot){
    if(leftRoot >= rightRoot) return 0;
    let mid = (leftRoot + rightRoot) >> 1; //获取中间值位置
    let ans = 0;
    // 统计获取左右区间对数添加到ans中
    ans += countReversePairs(nums,leftRoot,mid);
    ans += countReversePairs(nums,mid+1,rightRoot);
    // 横跨左右区间逆序对对数
    let k = leftRoot,p1 = leftRoot,p2 = mid +1;
    while((p1 <= mid) || (p2 <= rightRoot)){
        if((p2 > rightRoot) || (p1 <= mid && nums[p1] <= nums[p2])){
            temp[k++] = nums[p1++]
        }else{
            temp[k++] = nums[p2++];
            ans += (mid - p1 + 1);
        }
    }
        
    for(let i = 0 ; i <= rightRoot; i++)  nums[i] = temp[i]; return ans;
       
}

至此我们就完成了 leetcode-剑指 Offer 51-数组中的逆序对