单调栈的应用

110 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情

单调栈是一种和单调队列类似的数据结构。顾名思义就是栈内元素单调按照递增(递减)顺序排列的栈。

单调栈是一种简单但用法多种多样的算法。

在求单调递增或者单调递减的某种性质的序列时,可以表现出较好的性能

比如下面这道题:

股票价格跨度

编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。

今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。

例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]


题目要求我们动态的去解决,比当前小的连续子序列的长度,并且要求这个连续子序列持续到末尾

可以发现,我们可以很暴力的去循环枚举每一个数结尾时对应的子序列是多长

这样做,最坏情况下的时间复杂度应该是一个O(n2)O(n^2)时间的复杂度

我们得想办法去优化每次枚举循环的时间,其实我们可以发现一个问题,每次都利用到了相同的部分,若是后面这个数大于前面这个数的时候

其实关心的地方就仅仅只在于,当前这个数,前面的数比他大的有那几个数而已

那么,我们只需要维护一个单调递增的序列,即可完成这一目标

比如[10, 60, 60, 70, 60, 75, 85],我们只需要记录下来 [10, 60, 70, 75, 85] 这几个值即可,其他的值并不是那么的重要。

此题为了简单处理,我们插入一个边界值为正无穷大,保证栈不为空,然后依次动态处理即可


代码


use std::collections::{BTreeMap, BinaryHeap, HashMap, HashSet, VecDeque};
struct StockSpanner {
    ve: VecDeque<(i32, i32)>,
    idx: i32,
}
impl StockSpanner {
    fn new() -> Self {
        StockSpanner {
            ve: VecDeque::from(vec![(-1, 0x3f3f3f3f)]),
            idx: -1,
        }
    }

    fn next(&mut self, price: i32) -> i32 {
        self.idx += 1;
        while let Some(&x) = self.ve.back() {
            if x.1 <= price {
                self.ve.pop_back();
            } else {
                break;
            }
        }
        let ret = self.idx - self.ve.back().unwrap().0;
        self.ve.push_back((self.idx, price));
        ret
    }
}