一天一大 leet(计算右侧小于当前元素的个数)难度:困难-Day20200711

时间:2022-07-25
本文章向大家介绍一天一大 leet(计算右侧小于当前元素的个数)难度:困难-Day20200711,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目:

给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质:counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

示例

输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.

抛砖引玉

暴力循环

  • 声明一个存储结果的数组填充默认值 0
  • 逆向遍历输入数字
  • 对每个数组指定循环之后的数统计比其小的数字个数存储到存储结果的数组中
/**
 * @param {number[]} nums
 * @return {number[]}
 */
var countSmaller = function (nums) {
  let len = nums.length,
    _result = Array(len).fill(0)
  for (let i = len - 1; i >= 0; i--) {
    _result[i] = get_num(i, nums, nums[i])
  }
  function get_num(start, nums, n) {
    let num = 0
    if (start === nums.length - 1) return num
    for (let i = start; i <= nums.length - 1; i++) {
      if (nums[i] < n) num++
    }
    return num
  }
  return _result
}

不是吧?这样一道困难的题就完了?没那么简单,优化下吧

二分法查找

思路

上面的循环会发现i越小循环的次数越多,而且对右边一个数的比较越多 最后一位数需要参与len-1次比较

  • 如果能记录每一个位置的依次小于它的数,循环时我们只要知道新加入在哪两个数之间就可以之间得到它的结果
  • 查询一个数在哪两个数之间就演化成了排序的问题
  • 在一个数组里面最快定位一个数的位置,最快为:数组有序,二分法 则,逻辑就变成了:从nums从右向左取出元素到新数组排序,并且记录每一个数的位置

实现

  • 依次取出nums到一个新数字
  • 新数组为排序数组
  • 设取到nums[i],项新数组中插入时需要知道插入位置,插入位置即要求的右侧小于它的数
  • 插入时数据越多循环查询位置的次数越多,优化循环,借助二分法的思路求要插入的数据在新数组中的插入位置
/**
 * @param {number[]} nums
 * @return {number[]}
 */
var countSmaller = function (nums) {
  let len = nums.length
  if (len == 0) return nums
  let _result = Array(len).fill(0),
    dp = [];
  for (let i = len - 1; i >= 0; i--) {
    // 计算位置
    let index = findIndex(dp, nums[i])
    // 插入数据
    dp.splice(index, 0, nums[i])
    // 记录索引
    _result[i] = index
  }
  function findIndex(arr, target) {
    let start = 0,
        end = arr.length - 1;
    while (start < end) {
      let mid = parseInt((start + end) / 2,10)
      if (arr[mid] < target) {
        start = mid + 1;
      } else {
        end = mid;
      }
    }
    if (arr[start] < target) return start + 1;
    return start
  }
  return _result
}