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

力扣链接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/

题目描述

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

eg.  输入: [7, 5, 6, 4] ; 输出: 5

 

思路:归并排序

=> 对于两个已经排序的数组,如何利用归并排序寻找“跨数组”的逆序对?(如下面的 (2,1) (5, 1) (5, 4))

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
lPtr               rPtr

sorted: []
count: 0

此时两个指针指向的数字2>1,由于两个数组都是有序的,如果2比1大,那么左数组中,排在2之后的元素,一定也都比1大。 则左数组中所有可以与1组成逆序对的元素个数为4,count+4。

现在与1配对的逆序对已经找完,我们将rPtr右移,继续找与4配对的:

 

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
lPtr                   rPtr

sorted: [1]
count: 4

此时 2<4,由于两个数组都是有序的,如果2不能与4构成逆序对,也不可能与4之后的元素构成,包含2的逆序对已经找全,我们将lPtr右移:

 

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
    lPtr              rPtr

sorted: [1, 2]
count: 4

此时 3<4,与刚才同理,包含3的逆序对也已经找全(不存在),继续将lPtr右移:

 

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
        lPtr          rPtr

sorted: [1, 2, 3]
count: 4

此时出现 5>4, 则左数组中排在5以后的元素都可以与4构成逆序对,count+2,4的逆序对已找全,右移rPtr:

 

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
        lPtr              rPtr

sorted: [1, 2, 3, 4]
count: 6

5<6,右移lPtr:

 

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
            lPtr          rPtr

sorted: [1, 2, 3, 4, 5]
count: 6

7>6, count+1, 右移rPtr:

 

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
            lPtr              rPtr

sorted: [1, 2, 3, 4, 5, 6]
count: 7

7<8,右移lPtr:

 

[2, 3, 5, 7]     [1, 4, 6, 8]
 0  1  2  3       0  1  2  3
               lPtr           rPtr

sorted: [1, 2, 3, 4, 5, 6, 7]
count: 7

左边数组已经遍历结束,将右数组剩余元素写入sorted: [1, 2, 3, 4, 5, 6, 7, 8],归并排序结束,总计逆序对总数count为7。

 

 

由此可见,我们只需要在归并排序的归并阶段,加入上述统计逆序对个数的步骤即可。

 

代码:

/**
 * @param {number[]} nums
 * @return {number}
 */
var reversePairs = function(nums) {

    return mergeSort(nums, 0, nums.length-1);
    
};

var mergeSort = function(nums, left, right){
    if(left >= right)  return 0;
    
    let middle = Math.floor((right + left) / 2);
    
    let lCount = mergeSort(nums, left, middle);
    let rCount = mergeSort(nums, middle + 1, right);
    
    return lCount + rCount + mergeNCount(nums, left, middle, right);
}

var mergeNCount = function(nums, left, middle, right){
    let lPart = [];
    let rPart = [];
    
    let lSize = middle - left + 1;
    let rSize = right - middle;
    for(let li = 0; li < lSize; li++){
        lPart.push(nums[li + left]);
    }
    for(let ri = 0; ri < rSize; ri++){
        rPart.push(nums[ri + middle + 1]);
    }
    
    let lPtr = 0, rPtr = 0, p = left;
    let count = 0;
    while(lPtr < lSize && rPtr < rSize){
        if(lPart[lPtr] > rPart[rPtr]){
            count += lSize - lPtr;
            nums[p++] = rPart[rPtr++]; 
        } else {
            nums[p++] = lPart[lPtr++];
        }
    }
    
    while(lPtr < lSize){
        nums[p++] = lPart[lPtr++];
    }
    while(rPtr < rSize){
        nums[p++] = rPart[rPtr++];
    }
    
    return count;
}

 

posted @ 2021-04-19 00:50  studystudyxinxin  阅读(43)  评论(0编辑  收藏  举报