面试必考的基础数据结构梳理

栈属于基础数据结构之一,基础到仅用「后进先出」这四个字即可完整概括其核心特征。然而,基础并不代表着简单,「后进先出」的背后反而隐藏着多样的变化与极其广泛的应用。

在本篇文章中,我们将针对在基础栈上稍加改动所形成的「单调栈」算法进行详解。该算法与「单调队列」组成了算法题中最常考察的线性数据结构,属于面试中必知必会的算法知识。


首先我们来回忆一下「栈」。「栈」是一种「后进先出」的线性数据结构,其只有一端(栈顶)可以任意进出元素,而另一端(栈底)则无法进行任何操作。

如下图所示,3 1 4 5 2 7 依次入栈又依次出栈,其过程仅有栈顶在不断移动,其结果则满足「后进先出」的要求。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
单调栈
回忆完「栈」后,我们来进行「单调栈」的讲解。

什么是「单调栈」?顾名思义,「单调栈」就是栈内元素满足单调性的栈结构。此处的单调性分为单调递增与单调递减,为了便于描述,接下来以「单调递增栈」为例进行讲解。

「单调递增栈」就是栈内元素满足单调递增,假设当前元素为 x,若栈顶元素 ≤ x,则将 x 入栈,否则不断弹出栈顶元素,直至栈顶元素 ≤ x。

我们仍以 3 1 4 5 2 7 为例,其「单调递增栈」具体过程如下图所示。不难发现,入栈结束后,栈中仅保留了 1 2 7,其中 3 由于比 1 大被弹出,而 4 与 5 则由于比 2 大被弹出。
在这里插入图片描述
在这里插入图片描述
请大家仔细观看上述示例,理解清楚「单调递增栈」的具体操作后再往下看。

理解完上述示例后,我相信大家都会有一个疑问,即「在栈中维护单调性究竟有什么用呢」?

要回答这个问题,我们首先来观察一下上述示例中 2 为当前元素时的状态,如下图所示。
在这里插入图片描述
在该状态中,栈顶元素为 5,而当前元素为 2,由于 5 比 2 大,即此时若将 2 放入栈内,则不满足单调递增,因此需要将 5 弹出栈。这时请大家思考,2 和 5 之间是否有什么更深层的关系,所以才导致了 5 最终被 2 弹出?

仔细观察原始序列 3 1 4 5 2 7,我们可以发现 2 是 5 右边第一个比它小的数。基于这个发现,我们再次回顾「栈顶元素被弹出,当且仅当栈顶元素 > 当前元素」这一条件,因此我们可以得知对于单调递增栈,若栈顶元素被弹出,则当前元素为其右边第一个比它小的数。

2 弹出 5 后,栈顶变为 4,此时 4 仍比 2 大,因此 4 也被弹出,这时可确定 2 是 4 右边第一个比它小的数。

4 被弹出后,栈顶变为 1,1 ≤ 2,因此 2 被放入栈,此时栈内状态如下图所示。
在这里插入图片描述
按照刚才的思路,我们继续思考为何最终状态下 2 的左边是 1,这两个数之间又有何关联?

继续观察原始序列 3 1 4 5 2 7,可以发现 1 是 2 左边第一个小于等于它的数,稍加思考后,我们可以得知当一个数字被放入单调递增栈时,其栈内左边的数是它在原始序列中,左边第一个小于等于它的数。

至此我们可以解答最开始的疑问,单调栈的根本作用在于求得「每一个数字在原始序列中左 / 右边第一个大于 / 小于它自身的数字」,并且由于每一个数字只会入栈一次且最多出栈一次,因此总的时间复杂度为 O(n)。

另外需要注意,一次「单调递增栈」的过程,可以求得每个数字左边第一个小于等于它的数,以及右边第一个小于它的数,此处需注意「小于等于」和「小于」的区别。除此之外,「单调递减栈」将上述的「小于」改为「大于」即可成立。

总结
本篇文章主要讲解了「单调栈」算法,其中对于「单调递增栈」,我们在一遍扫描中求得每个数字左边第一个小于等于它的数,以及右边第一个小于它的数。另外,若想求得每个数字左边第一个小于它的数,则需要从右往左再扫描一遍数组。而对于「单调递减栈」,只需将上述的「小于」改为「大于」即可。

在文章的最后作者为大家整理了很多资料!包括java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板+Java架构实战电子书等等!
全部免费分享给大家,如有需要的朋友戳这里直接下载就好了,验证码:csdn
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值