最小栈的三种“实现”方式

Untitled.png

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

这是一道面试高频题,leetcode easy难度,这道题难在出栈时如何维护最小值,本文将介绍三种方式

  1. 维护最小值, 错误,因为它无法应对出栈情况.
  2. 基于辅助栈, 凑合用 时间复杂度O(1),空间复杂度O(n)
  3. 最小值+辅助栈. 前面两者优点的融合,精彩绝伦 时间复杂度O(1),空间复杂度O(1)

方式1 维护最小值

一个最容易想到也最简单的方式是维护一个数值表示最小值.每当有更小的数值入栈就将其更新.但是它无法应对出栈的情况.

例如我们将下图中的序号8,7依次出栈

序号 N 入栈值 E 最小值 M 出栈后的最小值
1 1 1
2 3 1
3 2 1
4 5 1
5 7 1
6 -1 -1
7 -2 -2 ?
8 4 -2 -2
  1. 第一次4出栈,大于当前最小值,可以判定最小值还是-2
  2. 第二次-2出栈,等于当前最小值, 无法判定新的最小值

这种方式无法实现但是其部分思路可以借鉴.

方式2 基于辅助栈

维护一个辅助栈,出入栈与原始栈保持一致,唯一的区别是: 入栈时辅助栈添加的是最小值

下面的图表演示了压栈/出栈时辅助栈是如何维护最新值: 最小值就是辅助栈栈顶元素

序号 N 入栈值 E 栈 S(栈顶<--栈底) 辅助栈 A(栈顶<--栈底) 最小值 M
1 1 1 1 1
2 3 3,1 1,1 1
3 2 2,3,1 1,1,1 1
4 5 5,2,3,1 1,1,1,1 1
5 7 7,5,2,3,1 1,1,1,1,1 1
6 -1 -1,7,5,2,3,1 -1,1,1,1,1,1 -1
7 -2 -2,-1,7,5,2,3,1 -2,-1,1,1,1,1,1 -2
8 4 4,-2,-1,7,5,2,3,1 -2,-2,-1,1,1,1,1,1 -2

先来看入栈

E[1]=1入栈,辅助栈中1入栈,栈顶为1,最小值为1

E[2]=3入栈,辅助栈中1入栈,栈顶为1,最小值为1

.....

E[6]=-1入栈,辅助栈中-1入栈.栈顶为-1,最小值为-1

再来看出栈流程

序号6出栈,辅助栈中-1出栈, 栈顶为1,最小值为1

序号5出栈,辅助栈中1出栈,栈顶为1,最小值为1.

辅助栈方式的时间复杂度是O(1),空间复杂度为O(n),下面介绍一种更为优秀的方式: 最小值+辅助栈,它的时间复杂度和空间复杂度均为O(1)

方式3 最小值+辅助栈

维护最小值的方式给我们的启示: 利用当前最小值和入栈值的关系可以推算新的最小值;辅助栈的方式给我们带来一些启示: 栈里存储的值可以不是入栈值.

结合这两点, 我们努力的方向是 寻找入栈值,最小值和存储值的关系.

在入栈时已知入栈值和最小值,求存储值.在出栈时由存储值和最小值求新的最小值.

看着比较绕,但是他的实质就是数学中的一个简单概念,已知X+Y=Z中的X,Y.求Z

难怪有人说京城三万月薪以下的码农只需要初中数学

下面我们就结合这个思路寻找特征

将序号为n的元素E[n]入栈时, 当前最小值M[n-1]和入栈值E[n]有如下关系.

入栈值  < 当前最小值   ==> 出现新的最小值 => E[n] < M[n-1]  ==> M[n-1] - E[n] > 0    @1
入栈值为第一个元素     ==> 出现新的最新值 ==> M[n] = E[n] @2
入栈值  >= 当前最小值  ==> 最小值不变 M[n]=M[n-1] ==> E[n] >= M[n-1]    ==> M[n-1]-E[n] <= 0 @3

我们可以将M[n-1]-E[n]当做存储值,由@1,@3得@4, 由@2得@5

存储值S[n] = M[n-1] - E[n]                  n > 1    @4     
                     = M[n] -E[n] = E[n] - E[n] = 0  n = 1     @5    

在出栈时根据存储值S[n]的正负推算出最小值,由于最小值的下标与另外两个值有错位,所以这里求的是M[n-1]下面这个两个公式结合入栈流程会更好懂

M[n-1]= M[n]+S[n]   S[n] > 0    @6
                M[n]        S[n] <= 0   @7

同理由公式@4,@5推算入栈值E[n]

E[n]= M[n-1] - S[n] n > 1 @8
        = M[n]          n = 1 @9 

有了上面的这些公式,我们可以得出方式3:

入栈时,存储的值S[n]由公式@4和@5计算得出;维护一个数值MIN表示最小值,每当有更小的值就将其更新

出栈时 由公式@6和@7更新最小值MIN,由公式@8,@9计算原始的入栈值

结合下面这个例子食用更佳

序号 N 入栈值 E 最小值 M 栈 S
1 1 1 0
2 3 1 -2,0
3 2 1 -1,-2,0
4 5 1 -4,-1,-2,0
5 7 1 -6,-4,-1,-2,0
6 -1 -1 2,-6,-4,-1,-2,0
7 -2 -2 1,2,-6,-4,-1,-2,0
8 4 -2 -6,1,2,-6,-4,-1,-2,0

首先,入栈流程 E[1]=1入栈

@5 =⇒ S[1]=0

@2 =⇒ MIN=M[1] = E[1] = 1

然后E[2]=3入栈

@4 ==> S[2] = M[1] -E[2] = -2
@3 ==> MIN = M[2] = M[1] = 1

...

之后E[6]=-1入栈

@4 ==> S[6] = M[5]-E[6] = 2
@1 ==> MIN = M[6] = E[6] = -1

再来看下出栈流程

S[8]=-6出栈

@7 ==> MIN = [7] = M[8] =-2
@8 ==> E[8] = M[7] - S[8]= -2-(-6) = 4

S[7]=1 出栈

@6 ==> MIN = M[6] = M[7] + S[7] = -2 +1 = -1
@8 ==> E[7] = M[6] -S[7] = -1 - 1 = -2

......

S[2]=-2出栈

@7 ==> MIN = M[1] = M[2] = 1
@8 ==> E[2] = M[1] -S[2] = 1-(-2) = 3 

S[1]=0出栈

M[0]无意义
@9 ==> E[1] = M[1] = 1

方式3是目前性能最佳的方式(双O(1)),也是最难理解的,笔者最初也是在leetcode上看的题解,读者可以结合例子和公式思考.

后记

本文介绍了最小栈的三种"解决"方式: 错误的方式,正确的方式,高效的方式.其中高效的方式结合了前两者的优点,达成了性能最优.

软件设计的哲学(A PHILOSOPHY OF SOFTWARE DESIGN)这本书有一章的主题是设计两次,本文特别契合这个主题,在这里跟大家分享下

尝试选择那些彼此截然不同的方法,这样你会学到更多。即使您确定只有一种合理的方法,无论您认为第二种设计有多糟糕,都要考虑采用第二种设计。思考该设计的弱点并将其与其他设计的特点进行对比将是有益的。


leetcode题目链接 https://leetcode-cn.com/problems/min-stack/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容

  • 栈 实现一个栈,该栈带有出栈(pop)、入栈(push)、取最小元素(getMin)3个方法。要保证这3个方法的时...
    杰伊_约翰阅读 334评论 0 0
  • 最小栈的实现 摘自漫画算法: 题目:实现一个栈,该栈带有出栈(pop)、入栈(push)、取最小元素(getMin...
    花逝97阅读 235评论 0 1
  • 题目: 实现一个栈,带有出栈(pop),入栈(push),取最小元素(getMin)三个方法。要保证这三个方法的时...
    zheting阅读 417评论 0 1
  • 题目 面试官:小夕,做一下这道面试题吧。小夕:好的,我可以借助辅助栈来实现吗?面试官:可以的,说一下你的思路吧。小...
    小夕学算法阅读 227评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,515评论 16 22