栈的特点是先进先出,只允许在一端插入和删除数据。

栈顶:栈的最后一个元素
栈底:栈的第一个元素
出栈:从栈顶删除最后一个元素
入栈:向栈顶压入新的元素

如何实现一个“栈”?

栈可以用数组实现,也可以用链表来实现。用数组实现的栈,我们叫作顺序栈,用链表实现的栈,我们叫作链式栈
下面是一个基于一个基于数组的顺序栈

  1. // 基于数组实现的顺序栈
  2. public class ArrayStack {
  3. private String[] items; // 数组
  4. private int count; // 栈中元素个数
  5. private int n; // 栈的大小
  6. // 初始化数组,申请一个大小为 n 的数组空间
  7. public ArrayStack(int n) {
  8. this.items = new String[n];
  9. this.n = n;
  10. this.count = 0;
  11. }
  12. // 入栈操作
  13. public boolean push(String item) {
  14. // 数组空间不够了,直接返回 false,入栈失败。
  15. if (count == n) return false;
  16. // 将 item 放到下标为 count 的位置,并且 count 加一
  17. items[count] = item;
  18. ++count;
  19. return true;
  20. }
  21. // 出栈操作
  22. public String pop() {
  23. // 栈为空,则直接返回 null
  24. if (count == 0) return null;
  25. // 返回下标为 count-1 的数组元素,并且栈中元素个数 count 减一
  26. String tmp = items[count-1];
  27. --count;
  28. return tmp;
  29. }
  30. }

时间、空间复杂度为O(1)

  1. function Stack(){
  2. var items = [];
  3. //入栈
  4. this.push = function(elem){
  5. items.push(elem);
  6. }
  7. //出栈
  8. this.pop = function(){
  9. return items.pop();
  10. }
  11. //返回栈顶元素
  12. this.peek = function(){
  13. return items[items.length -1];
  14. }
  15. //是否是空栈
  16. this.isEmpty = function(){
  17. return items.length;
  18. }
  19. //栈的长度
  20. this.size = function(){
  21. return items.length;
  22. }
  23. //清空栈
  24. this.clear = function(){
  25. items = [];
  26. }
  27. //打印栈
  28. this.print = function(){
  29. console.log(items.toString());
  30. }
  31. }

支持动态扩容的顺序栈

实现一个支持动态扩容的栈,只需要底层依赖一个支持动态扩容的数组就可以了。
image.jpeg

栈的应用

栈在函数调用中的应用

从代码中我们可以看出,main() 函数调用了 add() 函数,获取计算结果,并且与临时变量 a 相加,最后打印 res 的值。为了让你清晰地看到这个过程对应的函数栈里出栈、入栈的操作,我画了一张图。图中显示的是,在执行到 add() 函数时,函数调用栈的情况。
image.jpeg

栈在表达式求值中的应用

表达式求值是通过两个栈来实现,一个栈保存操作数,另一个栈保存运算符。

从左向右遍历表达式,当遇到数字时,将数字压入操作数栈;当遇到操作符时,与运算符栈的栈顶元素比较。

如果比运算符栈顶元素的优先级高,就将当前运算符压入栈;
如果比运算符栈顶元素的优先级低或者相同,从运算符中取栈顶运算符,从操作数栈的栈顶取2个操作数,然后进行计算,再把计算结果压入操作数栈,继续比较。

image.jpeg

栈在括号匹配中的应用

用来检测括号的嵌套是否合法

解答开篇

实现浏览器的前进和后退功能,使用两个栈(X和Y)实现。

  • 首次浏览的页面依次压入栈X
  • 当点击后退时,再依次从栈X中出栈,并将出栈的数据依次压入栈Y
  • 当点击前进时,依次从栈Y中出栈,并将出栈的数据依次压入X
  • 当栈X中没有数据时,说明没有页面可以继续后退浏览
  • 当栈Y中没有数据时,说明没有页面可以继续前进浏览

课后思考

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

参考答案:
08 | 栈:如何实现浏览器的前进和后退功能? - 图4