栈的引入简化了程序设计的问题,使得我们不需要像数组一样关注下标的增减,随机位置的插入与删除,在一些情况下可以以较小的时间复杂度解决问题,下面介绍一下栈常用的三个场景:递归,后缀表达式以及浏览器的前进后退。
目录
栈的应用(一)——递归
栈在程序设计语言中实现了递归,以斐波那契数列为例:
图一
图中1,1,2,3,5构成了一个序列,通过观察发现,相邻两项之和等于后一项,跟据递归的定义,我们可以总结出如下表达式 :
F(n)=0(n=0); F(n)=1 (n=1); F(n)=F(n-1)+F(n-2) (n>1)
通过递归的方式,我们可以以如下方式实现:
#include<stdio.h>
int fib(int n)
{
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
int main() {
int i;
for (i = 0; i < 10; i++)
printf("%d ", fib(i));
}
在递归过程中 ,到达程序出口之前,程序需要不断按顺序回退,执行递归行为,包括恢复前行过程中存储的数据。
由图可知,显然符合栈这样的数据结构,简单的说,在前行阶段,对于每一层递归,函数的局部变量,参数值以及返回地址都被压入栈中;在退回阶段,局部变量,参数值以及返回地址被弹出,由此实现递归。
栈的应用(二)——后缀表示法(逆波兰)
在计算机中,中缀表达式是人们常用的表达式形式,但计算机在计算表达式时,为了方便处理成对括号出现以及四则运算,需要将中缀表达式转换为后缀表达式。转换过程中使用栈来存储运算符,并按照运算符优先级进行弹出和压入操作。
举例:9+(3-1)*3+10/2;
中缀表示法:9+(3-1)*3+10/2;
后缀表示法:931-3*+102/+;
- 从左到右扫描表达式,遇到操作数时,将其压入栈中。
2.遇到运算符时,从栈中弹出两个操作数,进行运算,并将运算结果压入栈中(减法下减上,除法同理)
3.继续扫描表达式,重复步骤2,直到表达式结束
4.最后,栈中的唯一元素即为表达式的结果。
那么,如何判断符号放在哪个位置?规则如下:
遇到运算符时,判断栈是否为空。
- 如果栈为空,直接将运算符压入栈中。
- 如果栈不为空,则进行下一步。
比较当前运算符与栈顶运算符的优先级。
- 如果当前运算符的优先级大于栈顶运算符的优先级,将当前运算符压入栈中。
- 如果当前运算符的优先级小于或等于栈顶运算符的优先级,执行下一步。
从栈中弹出栈顶运算符,并将其输出(或保存到结果中)。
- 可以将弹出的运算符输出到逆波兰表达式的结果中,或者保存到其他地方。
重复步骤2和步骤3,直到当前运算符的优先级大于栈顶运算符的优先级,或者栈为空。
将当前运算符压入栈中。
整个过程充分利用了栈的先进后出的特性来处理,理解了这个过程也就理解了栈这个数据结构的基本操作。
栈的应用(三)——浏览器的前进后退
浏览器的前进后退:在浏览器中,可以通过前进和后退按钮进行页面的切换。浏览器使用栈来存储用户访问的页面,每次访问新页面时,将其压入栈中,点击后退按钮时,从栈顶弹出页面。
具体步骤如下:
-
创建两个栈:一个用来存储已访问的网页,称为backStack;另一个用来存储已经回退的网页,称为forwardStack。
-
当用户访问一个新的网页时,将该网页添加到backStack中。
-
当用户点击后退按钮时,从backStack中弹出最近访问的网页,并将弹出的网页添加到forwardStack中。
-
当用户点击前进按钮时,从forwardStack中弹出最近回退的网页,并将弹出的网页添加到backStack中。
-
当用户点击刷新按钮时,重新加载当前网页。
-
当用户点击清除历史记录按钮时,清空backStack和forwardStack。
-
通过使用栈的特性,当用户点击后退按钮时,从backStack中弹出最近访问的网页,将其添加到forwardStack中;当用户点击前进按钮时,从forwardStack中弹出最近回退的网页,将其添加到backStack中。这样就可以实现浏览器的前进后退功能。