[路飞]_数组中的逆序对

242 阅读2分钟

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

题目

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

示例1

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

题解

暴力求解

双重循环,找到所有可能的逆序对;思路没问题,但是50000的数据量,O(n2)O(n^2)肯定是超时的。

代码

var reversePairs = function(nums) {
    const len = nums.length;
    let result = 0;
    for(let i = 0 ; i < len ; i++){
        for(let j = i+1 ; j < len ; j++){
            if(nums[i] > nums[j])result++;
        }
    }
    return result

};

利用归并排序

先写个归并排序,然后改几行代码就可以;不难信我。

  • 归并排序的时候使用分治思想,总将数组分为左右两部分
  • 对于任意数组list,数组分为左半部分为left,右半部分为right;
  • 在递归划分数组的时候,使划分后的数组有序,什么时候数组有序呢?数组长度小于2的时候有序;
  • 当left与right有序的时候,开始合并left与right
  • 在合并的过程中计算【逆序】
  • 如何计算呢? 假设现在有数组left = [5],right = [3];
    left合并right过程中是不是[3,5],合并过程中,先放3,后放5;在放3的时候;left还有几个数?还有1个,则存在逆序对1个;

在假设:现在有数组left = [5,7],right = [3,6];
合并left和right,先放3,left长度为2;逆袭对+2; 因为3与5、7都构成逆序对 在放5,5逆序对不增加;
在放6,放6的时候left长度为1,逆序对+1;因为6与7构成逆序对对吧

所以在归并排序的合并上,只要right[r]中的值大于left[l]表示的值,此时right[r]的值可以与left构成(left.length - l)个逆序对。

根据上述思路,编辑代码如下:

代码

var reversePairs = function (nums) {
  let result = 0
  mergeSort(nums)
  return result
  function mergeSort(nums) {
    //获取数组长度
    const len = nums.length

    //如果数组长度小于2返回数组
    if (len < 2) return nums

    // 否则分割数组
    const mid = len >> 1 //右移1位,类似除以2

    // 递归分解左侧数组
    const left = mergeSort(nums.slice(0, mid))

    // 递归分解右侧数组
    const right = mergeSort(nums.slice(mid, len))

    // 合并左右两个数组
    return merge(left, right)

    // 合并数组
  }
  function merge(left, right) {
    let l = 0
    let r = 0
    let list = []
    while (l < left.length && r < right.length) {
      if (left[l] <= right[r]) {
        list.push(left[l])
        l++
      } else {
        list.push(right[r])
        result += left.length - l
        r++
      }
    }
    // 这两行代码省去了两个判断
    list = list.concat(left.slice(l, left.length))
    list = list.concat(right.slice(r, right.length))
    return list
  }
}