美文网首页
数据结构与算法笔记day05:栈

数据结构与算法笔记day05:栈

作者: 楠楠喜欢泡枸杞 | 来源:发表于2019-04-23 17:13 被阅读0次

    1什么是栈?

        后进者先出,先进者后出,是典型的“栈”结构。

        就像放盘子,我们是一个一个往上放,取的时候是从上到下一个一个来取。

        从它的操作特性来看,它可以说是一种“操作受限的线性表”,只允许在一端进行插入和删除操作。

        虽然数组和链表可以代替栈,但是它们暴露在外的操作接口过多,使用的时候比较不可控,容易发生错误。

        特定的数据结构是对特定场景的抽象,当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,我们就应该首选“栈”这种数据结构。

    2如何实现一个栈

        栈既可以用数组实现也可以用链表实现,用数组实现的栈叫顺序栈,用链表实现的栈叫链式栈

        用数组实现了一下,代码如下:

        我的github:OperatingStack.java

        那么它的时间复杂度和空间复杂度是什么呢?

        时间复杂度是O(1),不论是入栈还是出栈,都只涉及了栈顶元素的操作。

        空间复杂度也是O(1),只需要一个大小为n的数组和几个临时变量。

    3支持动态扩容的顺序栈

        支持动态扩容的数组:数组满了之后,申请一块更大的空间(2倍大的空间),将原来的数据拷贝进去。

        支持动态扩容的顺序栈,只需要底层依赖一个支持动态扩容的数组即可。

        原理如下图:

        对于入栈来说,它的时间复杂度是多少呢?

        当栈内有空闲空间时,为O(1),当栈满时,需要数据搬移,就变成了O(n),但是它的均摊时间复杂度依然是O(1)。因为,(假设栈原先的大小为k),当栈满后,申请了一个大小为2k的新的数组,然后对栈中的k的元素进行搬移,时间复杂度为O(n),但是在新的大小为2k的栈中,它的后面k-1个元素入栈时空间都是足够的,无需重新申请新的空间和数据搬移,所以均摊下来,时间复杂度是O(1)。

        这个例子也印证了前面讲的一个道理,最好时间复杂度一般都等于均摊时间复杂度。

        在大多数情况下,入栈操作的时间复杂度都是O(1),只有极个别情况下才会退化成O(n),我们把耗时比较多的操作的时间复杂度均摊到耗时少的上,平均情况下的时间复杂度就接近O(1)。

    4栈在函数调用中的应用

        操作系统为每个线程都分配了一块内存空间,这块内存被组织成“栈”这种结构,用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量以栈帧的形式入栈,当被调用函数执行完成,返回之后,它所对应的栈帧出栈。

        以下图的代码为例:    

        函数调用栈的过程:

    5栈在表达式求值中的应用

        对于算数表达式求值,编译器是通过两个栈来实现的。其中一个保存操作数的栈,一个保存操作符的栈。将算数表达式从左到右遍历,遇到数字则压入操作数栈,遇到操作符则与操作符栈顶元素相比较,若优先级比操作符栈顶元素高,则压入操作符栈,若优先级比操作符栈顶元素低或者相同,则取出操作符栈顶一个符号和操作数栈顶两个数字进行运算,结果压入操作数栈,然后继续向下遍历。

        以3+5*8-6为例,它的计算过程如下(我补充了其中省略的一小步):

    6栈在括号匹配中的应用

        我们还可以用栈来检查括号的匹配。

        比如{[{}]}或 [{()}([])] 等都为合法格式,,而{[}()] 或 [({)] 为非法的格式。

        从左到右依次遍历括号,遇到左括号则压入栈中,遇到右括号则从栈顶取出一个左括号进行匹配,若可以匹配则继续遍历,若不能匹配或者栈中没有元素则说明它为非法格式。所有的括号都遍历完之后,若栈中为空则为合法格式,若栈中还有元素则为非法格式。

    7栈实现浏览器的前进和后退功能

        用两个栈X和Y来实现,按照浏览网页的顺序将它们依次压入X栈中,当点击后退时,依次从X栈中取出元素并依次压入Y中,当点击前进时,则依次从Y中取出元素并依次压入X。当X/Y为空时,说明没有数据可以后退/前进浏览了。

        举例:

        依次浏览了a,b,c三个网页,则将它们依次压入X栈中。

        点击两次后退回到a页面,则c,b页面依次出栈,并依次压入Y栈中。

        点击前进查看b页面,则b出栈,压入X栈中。

        从b页面又进入了新的页面d,这个时候就无法再使用前进、后退功能来浏览c页面了,因此清空Y栈。

    内容小结

        栈是一种操作受限的数据结构,只支持入栈和出栈操作。后进先出是它最大的特点。栈既可以通过数组实现,也可以通过链表来实现。不管基于数组还是链表,入栈、出栈的时间复杂度都为 O(1)。除此之外,需要重点掌握支持动态扩容的顺序栈的均摊时间复杂度分析方法。

    思考

        1.为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?

        2.我们都知道,JVM 内存管理中有个“堆栈”的概念。栈内存用来存储局部变量和方法调用,堆内存用来存储 Java 中的对象。那 JVM 里面的“栈”跟我们这里说的“栈”是不是一回事呢?如果不是,那它为什么又叫作“栈”呢?

相关文章

网友评论

      本文标题:数据结构与算法笔记day05:栈

      本文链接:https://www.haomeiwen.com/subject/hkdugqtx.html