一、如何理解栈
举例说明一下,就是一摞叠在一起的盘子。我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也是从上往下一个一个地依次取,不能从中间任意抽出。后进者先出,先进者后出,这就是典型的“栈”结构。
栈是一种“操作受限”的线性表,只允许在一端插入和删除数据。当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,这时我们就应该首选“栈”这种数据结构。
二、如何实现一个栈
栈既可以用数组来实现,也可以用链表来实现。用数组实现的栈,我们叫作顺序栈,用链表实现的栈,我们叫作链式栈。
不管是顺序栈还是链式栈,入栈和出栈的操作时间复杂度都为O(1), 空间复杂度也为O(1)。
基于数组实现的顺序栈,必然要支持动态扩容。
三、栈的应用
栈在函数调用中的应用
比较经典的一个应用场景就是函数调用栈。每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈。
栈在表达式求值中的应用
关于与表达式求值,编译器就是通过两个栈来实现的,其中一个保存操作数的栈,另一个保存运算符的栈。我们从左到右遍历表达式,当遇到数字就直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较。如果运算符栈顶元素的优先级高,就将当前运算符压入栈;如果比运算符栈顶元素的优先级低或者相同,从运算符栈中取栈顶运算符,从操作数栈的栈顶取2个操作数,然后进行计算,再把计算完的结果压入操作数栈,继续比较。
栈再括号匹配中的应用
可以借助栈来检查表达式中的括号是否匹配, 用栈来保存未匹配的左括号,从左到有依次扫描字符串。当扫描到左括号时,则将其压入栈中;当扫描到右括号时, 从栈顶去除一个做括号。如果能匹配,比如"("和")"匹配,"["和"]"匹配,"{"和"}"匹配,则继续扫描剩下的字符串。如果扫描的过程中,遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式。