Loading

算法和数据结构 1 单调栈

单调栈

扯淡

计算机科学 和主流 数学 的区别在哪里?

计算机科学研究的对象是可数集合,是图灵机。他能够存取数据,每次进行有限步操作。图灵机的一个重要子集是下推自动机,下推自动机相较于有限状态机,区别在于多了一个,用来记录状态。

为什么用栈?我们知道,两个栈能模拟一个队列,两个队列也能模拟一个栈,这似乎说明两者有着某种相同的性质,单单从表面看,都不过是一种操作受限的线性表。

事实上,栈会把进入的数据反序,反两次就会变成顺序,因而两个栈模拟一个队列几乎没有任何性能损失;相反来看队列,为了做到先进后出,队列不得不以 O(n) 的复杂度来得到最先进入的元素。

这似乎说明栈有着比队列更为基础的性质。

单调栈和单调队列是进一步限定表内元素序关系的的线性表,跟逆序对一样,我几乎没见过哪本书详细剖析他们的性质。

反过来说,如果有哪本书详细讲到了,那么它名声却小到我都没有听说过,这实在是遗憾。

模板题

洛谷:P5788 【模板】单调栈

这里记录一下简单的思路:

  • 倒着遍历数组 a
    • 循环,若 a[i] >= stack.top() 则出栈。
    • 上一步停止时,若:
      1. stack.empty(),那么 a[i] 右边没有比他大的数
      2. !stack.empty(),那么 stack.top() 就是 a[i] 右边第一个比它大的数
    • stack.push(a[i])

这样的操作保证了两个事实:

  1. stack 是单调的
  2. 出栈操作停止时,栈顶元素(如果有)一定是第一个比自己大的元素

事实1 比较显然,下面看一下事实2

/// array /////////////////// top <- stack ////
... a, b ...                   c, d, e, f ...

数组遍历到 b ,出栈操作完成后,栈顶元素 c 必有 c > b。遍历到 a 时,b 已入栈:

/// array /////////////////// top <- stack ////
... a ...                      b, c, d, e, f ...

此时若

  1. a<b,那么 b 一定是 a 右边第一个比 a 大的元素
  2. a>=b,那之前被 b 出栈的元素就算不出栈,现在也要被出栈,所以没有影响

应用 1

Leetcode: 496. 下一个更大元素 I

nums1nums2 的子集,这就是模板题。

class Solution:
    def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        tmp = {}
        stk = []
        for i in reversed(nums2):
            while(stk and i >= stk[-1]):
                stk.pop()
            tmp[i] = stk[-1] if stk else -1
            stk.append(i)
        return [tmp[i] for i in nums1]

应用 2

posted @ 2021-10-27 15:41  人中之人  阅读(51)  评论(0)    收藏  举报