一天一大 leet(最小区间)难度:困难-Day20200801

img

题目:

你有 k 个升序排列的整数数组。找到一个最小区间,使得 k 个列表中的每个列表至少有一个数包含在其中。

我们定义如果 b-a < d-c 或者在 b-a == d-c 时 a < c,则区间 [a,b] 比 [c,d] 小。

示例:

输入:[[4,10,15,24,26], [0,9,12,20], [5,18,22,30]]
输出: [20,24]
解释:
列表 1:[4, 10, 15, 24, 26],24 在区间 [20,24] 中。
列表 2:[0, 9, 12, 20],20 在区间 [20,24] 中。
列表 3:[5, 18, 22, 30],22 在区间 [20,24] 中。

注意:

  1. 给定的列表可能包含重复元素,所以在这里升序表示 >= 。
  2. 1 <= k <= 3500
  3. -105 <= 元素的值 <= 105

抛砖引玉

img

思路

刚看本题确实没有什么思路,看了官方的题解先去完成了最小覆盖子串:
more-011: 最小覆盖子串 (难度:困难)

在回看思路就比较明显了:

  • 把nums矩阵合并成一个有序数组,且记录每一个元素来源的行数
  • 最终求覆盖所有行数的数字区间

逻辑

  • 即要记录value又要记录行数,数组(二维){index:行数,value:值}
  • 新排序数组dp
  • left,right新数组指针
  • map记录待匹配的行索引即每行数量
  • Rleft,Rright终止区间
/**
 * @param {number[][]} nums
 * @return {number[]}
 */
var smallestRange = function(nums) {
  let dp = [],
      map = new Map(),
      left = 0,
      right = 0,
      Rleft = -Number.MAX_VALUE, // 结果区间的left索引
      Rright = -1, // 结果区间的right索引
      type = 0;  // 区间中数字来源的行数种类

  // 生成二维映射数组dp  行映射map
  for (let i = 0; i < nums.length; i++) {
      map.set(i, 0);
      for (let j = 0; j < nums[i].length; j++) {
          dp.push({ index: i, value: nums[i][j] })
      }
  }

  // dp排序
  dp.sort((a, b) => a.value - b.value);

  // 右边界
  while (right < dp.length) {

      let Rindex = dp[right].index, // 指针所在的数字来源行
          Rvalue = dp[right].value,  // 指针所在的数字
          RMvalue = map.get(Rindex)  // 区间内改行数字数量

      // 区间新增 来源于新行 则行类型+1 
      if (RMvalue === 0) ++type

      // 记录对应新增数字个数
      map.set(Rindex, ++RMvalue)

      // 来源行包含所有行即等于map-size
      while (type === map.size && left <= right) {

          let Lindex = dp[left].index,
              Lvalue = dp[left].value,
              LMvalue = map.get(Lindex);

          // 如果新的区间比较小则使用新区间
          if (Rvalue - Lvalue < Rright - Rleft) {
              Rleft = Lvalue
              Rright = Rvalue
          }

          // 丢弃一个数字,map记录减一
          map.set(Lindex, --LMvalue)

          // 丢失数字的计算为0,则说明该来源行在区间中无数字,减少行计数
          if (LMvalue === 0) --type

          // 做区间向右移动
          left++
      }


      // 右区间向右移动
      right++

  }
  return [Rleft, Rright]
};

博客: 小书童博客

每天的每日一题,写的题解会同步更新到公众号一天一大 lee 栏目
欢迎关注留言

公号: 坑人的小书童

坑人的小书童

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值