栈
不知道大家有没有玩过手枪,估计都没有。其实我也没有,哈哈!现在和平年代,上哪去玩这种危险的真东西,就是仿真玩具也大都被限制了。几年之前,邻居家一个哥哥(下文就叫科哥吧)曾经当过兵,去他家玩的时候,还真就近距离的跟 枪 接触了一次,哈,当然不是用手摸了,而是学习了一下关于枪的知识。
科哥跟我说,早先军官们都爱用左轮手枪,而非弹夹式手枪,知道为啥吗,我不知道啊~~。他说因为子弹质量不过关,有个别可能是臭弹——也就是有问题 的、打不出来的子弹。弹夹式手枪,如果当中有一颗是卡住了的臭弹,那么后面的子弹就都打不了了。想想看,在你准备用枪的时候,那基本到了不是你死就是我亡的时刻,突然这手枪明明有子弹却打不出来,这不是要命吗?而左轮手枪就不存在这问题,这一颗不行,转到下一颗就可以了,人总不会倒霉到六颗全是臭弹。当然,后来子弹质量基本过关了,由于弹夹十多颗子弹,比左轮手枪的只能放 6 颗子弹要多,所以后来普及率更高的还是弹夹式的手枪。
栈就是类似弹夹中的子弹一样先进去,却要后出来,而后进的,反而可以先出来的数据结构。
在我们软件应用中,栈这种后进先出数据结构的应用是非常普遍的。比如你用浏览器上网时,不管什么浏览器都有一个“后退”键,你点击后可以按访问顺序的逆序加载浏览过的网页。比如你本来看着博客好好的,突然跳出来一个美女图片,你不小心点进去了,发现啥也没有,骗人的!此时你还想回去继续看新闻,就可以点击左上角的后退键。即使你从一个网页开始,连续点了几十个链接跳转,你点“后退”时,还是可以像历史倒退一样,回到之前浏览过的某个页面,如图:
很多类似的软件,比如Word、Photoshop等文档或图像编辑软件中,都有撤销(undo)的操作,也是用栈这种方式来实现的,当然不同的软件具体实现代码会有很大差异,不过原理其实都是一样的。
栈(stack)是限定仅在表尾进行插入和删除操作的线性表。
我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出(LastIn First Out)的线性表,简称LIFO结构。
理解栈的定义需要注意:
首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。定义中说是在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底。
它的特殊之处就在于限制了这个线性表的插入和删除位置,它始终只在栈顶进行。这也就使得:栈底是固定的,最先进栈的只能在栈底。
栈的插入操作,叫作进栈,也称压栈、入栈。如图:
栈的删除操作,叫作出栈,也有的叫作弹栈。如图:
那么我们来思考一个问题,这个最先进栈的元素,是不是就只能是最后出栈呢?
答案是不一定,要看什么情况。栈对线性表的插入和删除的位置进行了限制,并没有对元素进出的时间进行限制,也就是说,在不是所有元素都进栈的情况下,事先进去的元素也可以出栈,只要保证是栈顶元素出栈就可以。
举例来说,如果我们现在是有3个整型数字元素1、2、3依次进栈,会有哪些出栈次序呢?
第一种:1、2、3进,再3、2、1出。这是最简单的最好理解的一种,出栈次序为321。
第二种:1进,1出,2进,2出,3进,3出。也就是进一个就出一个,出栈次序为123。
第三种:1进,2进,2出,1出,3进,3出。出栈次序为213。
第四种:1进,1出,2进,3进,3出,2出。出栈次序为132。
第五种:1进,2进,2出,3进,3出,1出。出栈次序为231。
那么有没有可能是312这样的次序出栈呢?答案是肯定不会。因为3先出栈,就意味着,3曾经进栈,既然3都进栈了,那也就意味着,1和2已经进栈了,此时,2一定是在1的上面,就是更接近栈顶,那么出栈只可能是321,不然不满足123依次进栈的要求,所以此时不会发生1比2先出栈的情况。
大家看到了,只是3个元素,就有5种可能的出栈次序,如果元素数量多,其实出栈的变化将会更多的。这一点是需要好好琢磨的!
本文最后我们来看一下栈的抽象数据类型
对于栈来讲,理论上线性表的操作特性它都具备,可由于它的特殊性,所以针对它在操作上会有些变化。特别是插入和删除操作,一般叫做 push 和 pop,英文直译的话是压和弹,更容易理解。大家就把它当成是弹夹的子弹压入和弹出就好记忆了,我们一般叫进栈和出栈。
ADT 栈(stack)
Data
同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
Operation
InitStack(*S): 初始化操作,建立一个空栈S。
DestroyStack(*S): 若栈存在,则销毁它。
ClearStack(*S): 将栈清空。
StackEmpty(S): 若栈为空,返回true,否则返回false。
GetTop(S, *e): 若栈存在且非空,用e返回S的栈顶元素。
Push(*S, e): 若栈S存在,插入新元素e到栈S中并成为栈顶元素。
Pop(*S, *e): 删除栈S中栈顶元素,并用e返回其值。
StackLength(S): 返回栈S的元素个数。
endADT
了解线性表知识可以点击:数据结构之线性表,这一篇就够了,吐血总结,建议收藏~~