【leetCode】归并排序

【题目】

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

输入一个数组,求出这个数组中的逆序对的总数。

[7,5,6,4] => 5

【归并排序】

// 入口方法
function search(nums = []) {
  sort(nums, 0, nums.length - 1)
  return nums
}
// 归并排序中间方法,将数组分为两个数组,进行排序,排序完后再merge合在一起
function sort(nums, l, r) {
  if (l < r) {
    // 计算中间位置
    let mid = ((r - l) >> 1) + l
    // 左排序
    sort(nums, l, mid)
    // 右排序
    sort(nums, mid + 1, r)
    // 左右合并merge
    merge(nums, l, mid, r)
  }
}
// 比对合并拆分的两个数组
function merge(nums, l, mid, r) {
  // 左指针
  let i = l
  // 右指针
  let j = mid + 1
  // 临时数组
  let result = []
  // 指针在正常的数组中遍历
  while(i <= mid && j <= r) {
    // 当左数组的数值小于等于右数组的数值,将左数组的数值放进result
    if (nums[i] <= nums[j]) {
      result.push(nums[i])
      i++
    }
    // 当右数组的数值小于左数组的数值,将右数组的数值放进result
    else if (nums[i] > nums[j]) {
      result.push(nums[j])
      j++
    }
  }
  // 因为左/右数组已经走完,则将右/左数组的剩余参数放进result
  if (i > mid) {
    result.push(...nums.slice(j, r + 1))
  }
  else if (j > r) {
    result.push(...nums.slice(i, mid + 1))
  }
  // 到这里,result数组中存在的是已经排序好的数组
  // 再将排序好的数组放回nums中相对应的位置
  const rs = result
  nums.splice(l, rs.length, ...rs)
}

【逆序对】

在归并排序的基础上,计算每次数值对比后左边大于后边的次数,注意这里在计算次数的时候要判断左数组剩余数据的次数累加,防止漏加

// 入口方法
function search(nums = []) {
  let sum = 0
  return sort(nums, 0, nums.length - 1, sum)
}
// 归并排序中间方法,将数组分为两个数组,进行排序,排序完后再merge合在一起
function sort(nums, l, r, sum) {
  if (l < r) {
    // 计算中间位置
    let mid = ((r - l) >> 1) + l
    // 左排序
    sum = sort(nums, l, mid, sum)
    // 右排序
    sum = sort(nums, mid + 1, r, sum)
    // 左右合并merge
    sum = merge(nums, l, mid, r, sum)
  }
  return sum
}
// 比对合并拆分的两个数组
function merge(nums, l, mid, r, sum) {
  // 左指针
  let i = l
  // 右指针
  let j = mid + 1
  // 临时数组
  let result = []
  // 指针在正常的数组中遍历
  while(i <= mid && j <= r) {
    // 当左数组的数值小于等于右数组的数值,将左数组的数值放进result
    if (nums[i] <= nums[j]) {
      result.push(nums[i])
      i++
    }
    // 当右数组的数值小于左数组的数值,将右数组的数值放进result
    else if (nums[i] > nums[j]) {
      // 只有当右数组的数值小于(不等于)左数组的数值的时候,才计算逆序对的次数
      // 因为左数组的数值大于右数组,则左数组剩余的所有数都会大于,所以这里加的是左数组从i开始到mid结束的次数
      sum = sum + (mid - i + 1)
      result.push(nums[j])
      j++
    }
  }
  // 因为左/右数组已经走完,则将右/左数组的剩余参数放进result
  if (i > mid) {
    result.push(...nums.slice(j, r + 1))
  }
  else if (j > r) {
    result.push(...nums.slice(i, mid + 1))
  }
  // 到这里,result数组中存在的是已经排序好的数组
  // 再将排序好的数组放回nums中相对应的位置
  const rs = result
  nums.splice(l, rs.length, ...rs)
  return sum
}

【时间复杂度】

归并排序的时间复杂度为O(n*logn)

空间复杂度因为只使用了一个等长度的数组,所以是O(n)

【参考】

左程云左神的算法课程

一周刷爆LeetCode,算法大神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值