最小栈和最小队列

奇技指南

本文来自奇舞周刊,作者高峰,360奇舞团前端工程师,W3C性能工作组/WOT工作组成员。


相信栈和队列这两种数据结构大家一定都不陌生吧。这两种数据结构有个特殊形式:即最小栈和最小队列。

下面我们分别来介绍下这两个带有“特异功能”的数据结构。


01

最小栈

1

什么是最小栈

实现一个栈,带有出栈(pop),入栈(push),取最小元素(getMin)三个方法。并且这三个方法的时间复杂度都是O(1)。


2

设计思路

2.1 错误思路

看了上面对于最小栈的定义后,可能大部分人都能够快速想到这样的思路:

创建一个栈,再额外声明一个变量,用于存储栈的最小值。将第一个入栈的元素赋值给最小值变量。之后在每次进行入栈操作时,判断最小值变量与新入栈元素的大小。如果新入栈元素小于最小值变量,则修改最小值变量的值为新入栈元素。

这样我们就实现了一个栈,并且还会有一个变量指向栈的最小值。

不过遗憾的是,这个思路是错误的。因为当我们进行出栈操作的时候,如果最小值出栈了,那所声明的最小值变量就没有意义了。

所以我们在最小值出栈的时候,需要有更换顶替当前最小值的次最小值。

2.2 正确思路

上面说了,我们在最小值出栈时,需要有更换顶替当前最小值的值。我们可以通过两个栈来实现,第一个栈用于存放入栈的元素,第二个栈用于存放最小元素。

具体思路如下:

  1. 创建两个栈分别为A和B,A用于存放入栈的元素,B用于存放最小值在A中的下标或索引。

  2. 当第一个元素入A栈时,同时将其索引值推入B栈。

  3. 之后每个元素入栈A栈时,都将其与B栈栈顶的索引值对应的元素值比较,如果大于等于该元素,则忽略,如果小于该元素则入栈B。

  4. 这时,B栈的栈顶元素即为栈A的最小元素的索引。

  5. 每次元素出A栈时,如果出栈的元素的索引值等于B栈的栈顶元素,则将B栈的栈顶元素也出栈。出栈后,B栈顶的元素就会顶替出栈的最小值。

注意:B栈中之所以存索引,是因为这样可以规避一些问题。如当后加入的值和最小值相同时,如果我们不重复入栈B,那么出栈的时候对于B栈的判断就可能出问题(后入的等值元素可能会导致最小值提前出栈)。存索引就可以避免重复入栈最小值这个问题,节省一定的空间。


3

算法实现

为了便于展示,笔者使用Vue实现了这一算法。详情可以参见https://jsbin.com/lokedoxere。实现效果如下图:

640?wx_fmt=png

算法逻辑如下:

 
 

至此,我们实现了最小栈的逻辑。其实最小队列的实现思路与最小栈类似。不过又略有点不同。下面我们一起来看看吧。


02

最小队列的实现

1

什么是最小队列

实现一个队列,带有出队(deQueue),入队(enQueue),取最小元素(getMin)三个方法。要保证这三个方法的时间复杂度都尽可能小


2

设计思路

这里我就不卖关子了,直接进入正题。

我们可以通过两个队列实现,第一个队列用于存放入队的元素,第二个队列用于存放最小值。

详细思路如下:

  1. 创建两个队列分别为A和B,A用于存放入队的元素,B用于存放最小值(严格来说,B不能称作队列,它会将后入的元素删除)。

  2. 当第一个元素入队A时,同时将其入队B,因为这是就唯一一个值,所以是最小值。

  3. 之后每个元素入队A时,从B队尾开始比较,如果当前值大于等于(要有等于)B队尾值,则入队;否则,则删除B队尾值;然后再次比对B队尾值和当前值大小,直到当前值大于等于(要有等于) B队尾值或B为空,则当前值入队B尾。

  4. 这时每次B队中队首元素即为最小值。

  5. 当A中的元素出队时,如果元素值刚好与B队首元素相等,则B队首的元素也出队。(第三步中包含等于的判断,就是为了避免出队时等值元素不会出问题。)

  6. 这时deQueue/getMin时间复杂度为O(1)。enQueue为O(n)。

注意:不能像最小栈那样存索引,因为队列中的值的索引会变。所以重复的最小值要多次加入B。


3

算法实现

同样,笔者使用Vue实现了这一算法。详情可以参见https://jsbin.com/genigakudu。实现效果如下图:

640?wx_fmt=png

算法逻辑如下:



引申

上面我们介绍并实现了最小栈和最小队列,其实大家也可以用类似的方法实现出最大栈和最大队列。如果大家有兴趣就自己动手试试吧。



最新活动

640?wx_fmt=png

点击阅读原文立即报名


界世的你当不

只做你的肩膀

640?wx_fmt=jpeg 640?wx_fmt=jpeg

 360官方技术公众号 

技术干货|一手资讯|精彩活动

空·

640?wx_fmt=gif

点击“阅读原文”报名

右边给我一朵小花花

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值