[路飞]_单调栈及算法题应用

158 阅读2分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。

单调栈的概念

单调栈首先也是一个栈,符合栈的性质,只能在尾部进行插入和删除操作,并且始终保持栈内元素的有序

  • 例如有如下数组
var arr = [3, 1, 4, 5, 2, 9, 8, 12]
  • 此时需要维护一个单调栈,从栈底到栈顶依次递增,在将元素依次入栈的过程中,如果栈顶元素大于当前元素,则将栈顶元素出栈,循环这一过程,然后把当前元素入栈
var stack = []
for (var i = 0; i < arr.length; i++) {
  var tmp = arr[i]
  while(stack.length && stack[stack.length - 1] >= tmp) {
    stack.pop()
  }
  stack.push(tmp)
}
  • 最终得到一个单调递增的栈
[1, 2, 8, 12]
  • 可以发现栈内元素在原数组中左侧第一个小于它的元素都是单调栈内的前一个元素

图片.png

  • 因此单调栈适合维护最近(大于/小于)问题

    • 数组从左侧入栈就是维护左侧最近关系
    • 数组从右侧入栈就是维护右侧最近关系
  • 对比之前学过的单调队列,则适合解决区间内的最值

力扣算法题

496. 下一个更大元素 I

图片.png

  • 这题显然要求的是右侧最近大于问题,因此可以用到单调栈,并且从右侧入栈
  • 根据题目要求,最终要返回 nums1 元素在对应原数组 nums2右侧第一个大于当前元素的值,因此可以额外用一个哈希表来记录原数组 nums2 右侧第一个大于当前元素的值,最终遍历哈希表找出对应的值即可
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var nextGreaterElement = function(nums1, nums2) {
  var stack = [],  // 单调栈
      map = {} // 哈希表
  for(var i = nums2.length - 1; i >=0; i--) { // 从数组右侧开始入栈
    var tmp = nums2[i]
    // 维护单调性,剔除栈内比当前元素小的元素
    while(stack.length && stack[stack.length - 1] <= tmp) {
      stack.pop()
    }
    if(stack.length) { // 栈顶元素就是右侧第一个大于当前元素的值
      map[tmp] = stack[stack.length - 1]
    } else {
      map[tmp] = -1
    }
    stack.push(tmp)
  }
  var ans = []
  for(var val of nums1) {
    ans.push(map[val])
  }
  return ans
};