算法笔记35:数组中的逆序对

131 阅读1分钟

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

题目的思路很巧妙,是通过归并排序的特殊性质来实现低时间复杂度的逆序数对查找。精髓在于归并排序过程中,会把左右两部分都已经排好序的数组进行合并,而这个合并的过程,就可以确定逆序对的数量并进行累加。

index   :   [0, 1, 2, 3, 4, 5, 6] 
element :   [4, 7, 9][1, 2, 6, 8] 
array   :   [...a...][....b.....]
pointers:      [i]         [j]
merged  :   [1, 2, 4]

以上面这个情况为例,我们已经把 1, 2, 4 三个元素合并好了,然后我们看 i 指向的元素 7j 指向的元素 6

如果我们在数组 b 中找到了比数组 a 还小的数字,我们就发现了逆序对。总共有几个逆序对呢?数组 a 里还剩几个数,就有几个。也就是 [..., 7, ... , 6, ...][..., 9, ..., 6, ...] 。我们不需要管中间出现了多少别的数,在这次迭代中能找到就累加即可。

代码如下:

const reversePairs = (nums) => {
    // array for temp value storage
    const tmp = Array(nums.length).fill(null);

    const mergeSort = (left, right) => {
        // end condition
        if (left >= right) {
            return 0;
        }

        const mid = Math.floor((left + right) / 2);
        // recursively sort
        let res = mergeSort(left, mid) + mergeSort(mid + 1, right);

        // fill up current interval with numbers already sorted in last call
        for (let k = left; k <= right; k++) {
            tmp[k] = nums[k];
        }
        let i = left;
        let j = mid + 1;
        for (let k = left; k <= right; k++) {
            if (i > mid) {
                // if all items in the left array is added,
                // fill in the rest with items in the right array
                nums[k] = tmp[j];
                j++;
            } else if (j > right) {
                // if all items in the right array is added,
                // fill in the rest with items in the left array
                nums[k] = tmp[i];
                i++;
            } else {
                if (tmp[i] <= tmp[j]) {
                    // current left item is less or equal than current right item,
                    // which is sorted so just merge
                    nums[k] = tmp[i];
                    i++;
                } else {
                    // current left item is greater than current right item,
                    // where reversed pairs need to be counted
                    nums[k] = tmp[j];
                    j++;
                    res += mid - i + 1;
                }
            }
        }

        return res;
    }

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