数组中的逆序对

590 阅读2分钟

题目描述

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

解题思路

算法

归并排序,在“并”的时候求逆序对

思路

本体要统计的是逆序对的个数,我们在归并排序过程中,对合并的两部分分别统计逆序对个数,然后再统计两个部分之间的逆序对个数,把他们 +=,就可以得到我们的结果

过程

在归并的过程中,将数组分为左和右两部分,根据归并排序的定义,两部分区间为:

  • 左:[l, mid]
  • 右:[mid + 1, r]

我们要求逆序对,实际上就是求左,右两部分分别有多少个逆序对,以及左右跨区间之间有多少个逆序对,因此结果是对 3 部分统计的加和,我们用 ans 来统计

接下来,我们来看一个区间怎么来进行统计

我们统计的过程发生在合并时,右区间当前指针指向的是相对小值时发生,例如:

[1,3(p1),4], [2(p2), 2, 3]

p1, p2 指针分别指向左右区间元素,当两指针指向为上述情况时,可见在左区间右 mid - p1 + 1 个元素大于 p2 指向的元素,我们在移动 p2 之后,进行统计

代码

/**
       * @param {number[]} nums
       * @return {number}
       */
      var reversePairs = function (nums) {
        return countResult(nums, 0, nums.length - 1)
      }

      function countResult(nums, l, r) {
        // if the section length <= 1, reverse pairs number is 0
        if (l >= r) return 0
        let ans = 0
        const mid = (l + r) >> 1,
          temp = []

          // pairs in the left section
          ans += countResult(nums, l, mid)
          // pairs in the right section
          ans += countResult(nums, mid + 1, r)
          
          // pairs across the left & right
          let k = l,
          p1 = l,
          p2 = mid + 1
          while (p1 <= mid || p2 <= r) {
            if (p2 > r || (p1 <= mid && nums[p1] <= nums[p2])) {
              temp[k++] = nums[p1++]
            } else {
              temp[k++] = nums[p2++]

              ans += mid - p1 + 1
          }
        }

        for (let i = l; i <= r; i++) {
          nums[i] = temp[i]
        }
        return ans
      }