实现括号匹配校验和滑动窗口最大值。

先理解原理和示例,再练习完整口述与追问。

括号匹配用栈:左括号入栈,右括号时弹出栈顶并检查是否配对,遍历结束栈必须为空。滑动窗口最大值用单调递减双端队列保存下标:新元素入队前弹出所有不大于它的队尾,队头下标滑出窗口时移除,队头即当前窗口最大值,整体 O(n)。

括号的“最近未闭合的左括号必须先闭合”正好是后进先出,所以用栈。只处理三种括号,其他字符跳过;提前遇到右括号而栈为空时 pop() 返回 undefined,不等于任何左括号,直接返回 false。

滑动窗口中,如果一个元素比它左边的元素更大,左边的那个在之后的任何窗口中都不可能成为最大值,可以丢弃,因此队列从头到尾保持递减。每个下标最多入队、出队各一次,所以是线性时间。JavaScript 数组的 shift 是 O(n),用 head 指针代替队头出队,保持整体线性。k 不合法时抛出 RangeError,比返回空数组更容易发现调用错误。

js
function isValidBrackets(text) {
  const pairs = { ')': '(', ']': '[', '}': '{' }
  const stack = []
  for (const char of text) {
    if (char === '(' || char === '[' || char === '{') stack.push(char)
    else if (char === ')' || char === ']' || char === '}') {
      if (stack.pop() !== pairs[char]) return false
    }
  }
  return stack.length === 0
}

function maxSlidingWindow(nums, k) {
  if (!Number.isInteger(k) || k < 1 || k > nums.length) {
    throw new RangeError('k 必须是 1 到数组长度之间的整数')
  }
  const deque = []
  let head = 0
  const result = []
  for (let i = 0; i < nums.length; i++) {
    while (deque.length > head && nums[deque[deque.length - 1]] <= nums[i]) deque.pop()
    deque.push(i)
    if (deque[head] <= i - k) head++
    if (i >= k - 1) result.push(nums[deque[head]])
  }
  return result
}

推演:maxSlidingWindow([1, 3, -1, -3, 5, 3, 6, 7], 3) 得到 [3, 3, 5, 5, 6, 7];在 i = 4 时,5 入队前把 -3、-1、3 全部弹出,队头变为 5。

括号题只比较数量而不检查顺序,会把 ([)] 误判为合法;滑动窗口中队列存值而不是下标,就无法判断队头是否已滑出窗口。

括号匹配用栈,左括号入栈,遇到右括号就弹出栈顶看是否配对,最后栈要为空。滑动窗口最大值用单调递减的双端队列存下标:新元素进来前把队尾不大于它的都弹掉,因为它们以后不可能是最大值;队头下标滑出窗口就移除;窗口形成后队头就是最大值。每个元素进出队列各一次,所以是 O(n),JS 里用 head 指针避免 shift 的开销。