id="c_n9"width="1920"height="990"style="position: fixed; top: 0px; left: 0px; z-index: -1; opacity: 0.5;">

单调栈

单调栈

所谓单调栈就是栈中的元素具有一定的单调性,像这样:

image-20230410152711773

利用这种数据结构的解决的问题大多都是在数组中寻找某一个数左边或者右边最近的最大或者最小的一个数

从个人做题的角度来看,这类题目转换为左边的问题相对好思考一些(右边的直接reverse一下,也可以转换为左边的问题)

题目

假定有数组\(a\),你需要寻找\(a_i\)左边大于和小于\(a_i\)的最近的数

暴力做法

毫无疑问就是两重循环,但是在第二重循环中是存在可以优化的地方的。所以也就引出了单调栈这一数据结构。

情况01

对于寻找左边大于\(a_i\)的 这种情况,第二重循环有些元素是不会用到的,具体的情况就是当存在\(a_j \leqslant a_k\)\(j < k\)\(a_j > a_i , a_j > a_i\),那么其实\(a_j\)其实已经就没有价值了,那么就可以从\(stack\)中弹出了。图如下:

image-20230410154749329

var stack [N]int 
var top int 
for idx, v := range a {
    for top != 0 && stack[top] <= v {
        top--
    }
    if top != 0 {
        fmt.Println(stack[top])
    } else {
        fmt.Println(-1)
    }
    top++
    stack[top] = v
}

情况02

其实和第一种情况就是相反的,当\(a_j > a_k, j < k, a_j,a_k<a_i\),那么\(a_j\)就是永远都用不到的,所以直接冲\(stack\)中弹出即可。由于是相反的,故图&代码便不再给出。

Q&A

  • 为什么用\(stack\)

    因为第二重循环是倒序遍历的,其实这里符合\(stack\)的特性,也就是符合后进先出的性质;故此处使用\(stack\)

参考资料

posted @ 2023-04-10 16:06  hellozmc  阅读(12)  评论(0编辑  收藏  举报