用JavaScript刷leetcode第1124题-表现良好的时间段(前缀和 + 贪心)

134 阅读2分钟

一、 前言

  • 求区间和问题转换成求相应前缀和然后做差
  • 最大区间,可以用贪心,左边界越小越好,右边界越大越好

二、题目描述

原谅我比较懒,我直接从leetcode截图,欲知更加清楚的描述,请看leetcode题目链接

image.png

三、思路

  • 首先入参是每天的工作时间,我们并不关注具体时间,只关注这一天是否是劳累的一天,大于8小时的为劳累的一天记作1,小于8小时的为不劳累的一天记作-1。举个例子:就是把[9,9,6,0,6,6,9]映射成[1,1,-1,-1,-1,-1,1]
  • 将映射后的数组转换成前缀和数组。即[1,1,-1,-1,-1,-1,1]变成[0,1,2,1,0,-1,-2,-1]
  • 贪心,区间求和,左边界越小越好,右边界越大越好
  • 从左往右扫描前缀和数组,初始化一个栈,存前缀和数组中符合要求的下标。
  • 这个要求就是左边界越小越好,故存前缀和数组中依次递减的元素的下标(注意这里不是单调递减栈,因为其存的是下标,是单调递增的)
  • 从右往左扫描前缀和数组,右边界越大越好。找出比栈中元素大的出栈,此时为最右侧。求差值然后与已记录得最大区间值比较,得最大区间值。

四、代码

git代码链接

/**
 * @param {number[]} hours
 * @return {number}
 */
const longestWPI = function(hours) {
  // 这里直接求前缀和数组,好处是少遍历一次,坏处是不好理解了。其实就是求映射和前缀和这两步的合并
  const preSum = new Array(hours.length + 1).fill(0);
  for(let i = 0; i < hours.length; ++i) {
    preSum[i+1] = hours[i] > 8 ? preSum[i] + 1 : preSum[i] - 1;
  }
  
  // 前缀和中依次递减元素的下标栈, 初始化最小元素是第0项
  const indexStack = [0]
  // 从左往右扫描前缀和数组, 存前缀和中符合要求元素的下标,由于已经初始化过,故从索引为1的元素往后扫描
  for(let i = 1; i < preSum.length; ++i) {
    if(preSum[i] < preSum[indexStack[indexStack.length - 1]]) indexStack.push(i)
  }
  
  // 区间最大结果,初始化为0
  let max = 0;
  // 从右往左扫描,找右边界最大
  for(let i = preSum.length - 1; i > max; --i) {
    while(preSum[i] > preSum[indexStack[indexStack.length - 1]]) {
      max = Math.max(max, i - indexStack.pop())
    }
  }
  return max
}