【考研】栈在表达式求值中的应用(真题分析)

打印 上一主题 下一主题

主题 972|帖子 972|积分 2916

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
媒介

直接以例题分析的方式,说明 “ 栈在表达式求值中的应用 ” 的知识点。
本文内容源于对《数据结构(C语言版)》(第2版)、王道讲解学习所得心得、条记整理和总结。
可搭配以下链接一起学习:
【考研】《数据结构》知识点总结.pdf_考研数据结构知识点总结pdf


目次

媒介
一、基本概念
二、习题


一、基本概念

1、中缀表达式有括号,后缀表达式无括号
中缀表达式不仅依靠运算符的优先级,而且还要处置惩罚括号。后缀表达式的运算符在操纵数后面,在后缀表达式中已考虑了运算符的优先级,没有括号,只有操纵数和运算符。
举例:中缀表达式 A+B* (C-D)-E/F 所对应的后缀表达式为 ABCD-*+EF/-(见下图)
后缀表达式类似原运算式对应的表达式树(用来表示算术表达式的二元树)的后序遍历

 2、通事后缀表示计算表达式值的过程
顺序扫描表达式的每一项, 然后根据它的类型做如下相应操纵:
(1)若该项是操纵数,则将其压入栈中;
(2)若该项是操纵符 <op>,则连续从栈中退出两个操纵数Y和X,形成运算指令x<op>Y,并将计算结果重新压入栈中。
(3)当表达式的所有项都扫描并处置惩罚完后,栈顶存放的就是末了的计算结果。
举例:
后缀表达式为 ABCD-*+EF/- 的求值过程需要 12 步:
扫描项项类型动作栈中内容
1置空栈
2A操纵数进栈A
3B操纵数进栈A B
4C操纵数进栈A B C
5D操纵数进栈A B C D
6-操纵符D、C 退栈,计算 C - D,结果 R1 进栈A B R1
7*操纵符R1、B 退栈,计算 B * R1,结果 R2 进栈A R2
8+操纵符R2、A 退栈,计算 A+R2,结果 R3 进栈R3
9E操纵数进栈R3 E
10F操纵数进栈R3 E F
11/操纵符F、E 退栈,计算 E / F,结果 R4 进栈R3 R4
12-操纵符R4、 R3退栈,计算R3-R4,结果R5进栈R5
二、习题

   1. 栈的应用不包罗(     D F     )。
  A. 递归                          B. 进制转换                     C. 迷宫求解      
  D. 缓冲区队列               E. 括号匹配                     F.页面更换算法
  解:缓冲区和页面更换算法中的 FIFO 都用队列实现。
   2. 【2009统考真题】为解决计算机主机与打印机之间速率不匹配的问题,通常设置一个打印数据缓冲区,主机将要输出的数据依次写入该缓冲区,而打印机则依次从该缓冲区中取出数据。该缓冲区的逻辑结构应该是(     B     )。
  A. 栈                          B. 队列                          C. 树                                D. 图
    3. 表达式 a*(b+c)-d 的后缀表达式是(      B     )。
  A. abcd*+-                   B. abc+*d-                   C. abc*+d-                           D. -+*abcd
  解: 画出运算式对应的表达式树,用后序遍历该树得出后缀表达式。选B,如下图:

   4. 使用栈求表达式的值时,设立运算数栈 OPEN,假设 OPEN 只有两个存储单元,则在下列表达式中,不会发生溢出的是(       B     )。
  A. A-B*(C-D)             B.  (A-B)*C-D            C. (A-B*C)-D                 D. (A-B)*(C-D)
  解: 使用栈求表达式的值时,可分别设立运算符栈和运算数栈,其原理稳固。
对选项B:
A入栈,B入栈,计算 A - B 得 R1;
C入栈,计算 R1* C 得 R2; 
D入栈,计算 R2 - D 得 R3,由此栈深为 2 。符合题意,不会发生溢出。
对选项A:
A入栈,B入栈,C入栈,D入栈,计算 C - D 得 R1;可知栈深为4。溢出。
对选项C:
A入栈,B入栈,C入栈,计算 B * C,得 R1;可知栈深为3。溢出。
对选项D:
A入栈,B入栈,计算 A - B 得 R1;
C入栈,D入栈,此时栈中有 R1 C D,可知栈深为3。溢出。
   5. 下列说法中,精确的是(     A     )
  A. 消除递归不肯定需要使用栈
  B. 对同一输入序列举行两组差别的正当入栈和出栈组合操纵,所得的输出序列也肯定相同
C. 通常使用队列来处置惩罚函数或过程调用
D. 队列和栈都是运算受限的线性表,只允许在表的两头举行运算
  解:使用可以模仿递归的过程,以此来消除递归,但对于单向递归和尾递归而言,可以用选代方式来消除递归,A对;
差别的进栈和出栈组合操纵,会产生许多差别的输出序列,B错;
通使用栈来处置惩罚函数或过程调用,C错;
队列和栈都是操纵受限的线性表, 但只有队列允许在表两头举行运算。 而栈只允许在栈顶方向举行操纵,D错。
 
   6. 【2012 统考真题】已知操纵符包罗+、一、*、 /、(和)。将中缀表达式a+b-a*((c+d)/e-f)+g转换为等价的后缀表达式 ab+acd+e/f-*-g+ 时,用栈来存放临时还不可以或许定运算序次的操纵符。若栈初始时为空,则转换过程中同时保存在栈中的操纵符的最大个数是(      A     )。
A. 5                              B. 7                             C. 8                                D. 9
  解:将中缀表达式转换为等价的后缀表达式:(算法头脑可参考第7题)
可知在转换过程中同时保存在栈中的操纵符的最大个数是 5 个,此时栈中元素为 -*((+
待处置惩罚序列后缀表达式当前扫描动作
a+b-a*((c+d)/e-f)+gaa 加入后缀表达式
+b-a*((c+d)/e-f)+ga++入栈
b-a*((c+d)/e-f)+g+abb 加入后缀表达式
-a*((c+d)/e-f)+g+ab--优先级低于栈顶的+,弹出+
-a*((c+d)/e-f)+gab+--入栈
a*((c+d)/e-f)+g-ab+aa 加入后缀表达式
*((c+d)/e-f)+g-ab+a**优先级高于栈顶的-,*入栈
((c+d)/e-f)+g-*ab+a((入栈
(c+d)/e-f)+g-*(ab+a((入栈
c+d)/e-f)+g-*((ab+acc 加入后缀表达式
+d)/e-f)+g-*((ab+ac+栈顶 ( 的优先级低于+,+入栈 
d)/e-f)+g-*((+ab+acdd 加入后缀表达式
)/e-f)+g-*((+ab+acd)把栈中第二个 ( 之前符号加入表达式
/e-f)+g-*((ab+acd+/栈顶 ( 的优先级低于/,/入栈
e-f)+g-*(/ab+acd+ee  加入后缀表达式
-f)+g-*(/ab+acd+e-栈顶 / 的优先级高于-,弹出/
-f)+g-*(ab+acd+e/-栈顶 ( 的优先级低于-,-入栈
f)+g-*(-ab+acd+e/f f 加入后缀表达式
)+g-*(-ab+acd+e/f)把栈中 ( 之前的符号加入表达式
+g-*ab+acd+e/f-+栈顶 * 的优先级高于+,弹出*
+g-ab+acd+e/f-*+栈顶 - 的优先级高于+,弹出-
+gab+acd+e/f-*-++入栈
g+ab+acd+e/f-*-gg 加入后缀表达式
+ab+acd+e/f-*-g扫描完毕,运算符依次退栈加入表达式
ab+acd+e/f-*-g+完成
   7. 【2014统考真题】假设栈初始为空,将中缀表达式 a/b+(c*d-e*f)/g 转换为等价的后缀表达式的过程中,当扫描到 f 时,栈中的元素依次是(      B     ).
A. + ( * -                         B. + ( - *                   C. / + ( * - *                         D.  / + - * 
   解:将中缀表达式转换为后缀表达式的算法头脑如下:
(1)从左向右开始扫描中缀表达式;
(2)遇到数字时,加入后缀表达式;
(3)遇到运算符时:
a. 若为 '(',入栈;
b. 若为 ')',则依次把栈中的运算符加入后缀表达式直到出现 '(',从栈中删除 '(';
c. 若为除括号外的其他运算符,当其优先级高于除 '(' 外的栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处置惩罚的运算符优先级高和优先级相称的运算符,直到一个比它优先级低的或遇到了一个左括号为止。
(4)当扫描的中缀表达式结束时,栈中的所有运算符依次出栈加入后缀表达式。
待处置惩罚序列后缀表达式当前扫描元素动作
a/b+(c*d-e*f)/gaa 加入后缀表达式
/b+(c*d-e*f)/ga//入栈
b+(c*d-e*f)/g/abb 加入后缀表达式
+(c*d-e*f)/g/ab++优先级低于栈顶的/,弹出/
+(c*d-e*f)/gab/++入栈
(c*d-e*f)/g+ab/((入栈
c*d-e*f)/g+(ab/cc 加入后缀表达式
*d-e*f)/g+(ab/c*栈顶为(,*入栈
d-e*f)/g+(*ab/cdd 加入后缀表达式
-e*f)/g+(*ab/cd--优先级低于栈顶的*,弹出*
-e*f)/g+(ab/cd*-栈顶为(,-入栈
e*f)/g+(-ab/cd*ee 加入后缀表达式
*f)/g+(-ab/cd*e**优先级高于栈顶的-,*入栈
f)/g+(-*ab/cd*eff 加入后缀表达式
)/g+(-*ab/cd*ef)把栈中 ( 之前的符号加入表达式
/g+ab/cd*ef*-//优先级高于栈顶的+,/入栈
g+/ab/cd*ef*-gg 加入后缀表达式
+/ab/cd*ef*-g扫描完毕,运算符依次退栈加入表达式
ab/cd*ef*-g/+完成
由此可知,当扫描到f时,栈中的元素依次是+(-*,选B。
在此,以上面给出的中缀表达式为例,给出中缀表达式转换为前缀或后缀表达式的手工做法
中缀表达式 a/b+(c*d-e*f)/g
步调1:按照运算符的优先级对所有的运算单元加括号。
式子变成 ((a/b)+(((c*d)-(e*f))/g))。
步调2:转换为前缀或后缀表达式。
前缀:把运算符号移动到对应的括号前面,式子变成+ (/ (ab)/(-(* (cd)* (ef))g))。
把括号去掉: +/ab/-*cd*efg 前缀式子出现。
后缀:把运算符号移动到对应的括号后面,式子变成((ab)/(((cd)*(ef)*)-g)/)+。
把括号去掉:ab/cd*ef*-g/+后缀式子出现。
当题目要求直接求前缀或后缀表达式时,这种方法会比上一种方法快捷得多。
当然,画出运算式子的表达式树,用前序遍历得前缀表达式,用后序遍历得后缀表达式会更快。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

写过一篇

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表