当前位置: 首页 > news >正文

11.9 表达式求值

11.9 表达式求值

【问题描述】给出一个合法的表达式,请输出它的计算结果。表达式满足以下条件:
① 只有+、-、*、/、^(乘方)运算和括号。
② 所有操作数都是非负数。计算过程中(包括最后结果)可能会出现负数,但不会超过 int 的表示范围。
③ 表达式开头、结尾或表达式内部可能有多余的空格。

(1) 模拟

设两个栈,一个是操作数栈,用来存放操作数,如 3、4、8 等,另一个是运算符栈,用来存放运算符。
首先,将“(”压进运算符栈的栈底①。然后,依次扫描,按照栈的后进先出原则进行:
① 遇到操作数,进操作数栈;
② 遇到运算符时,则需将此运算符的优先级与栈顶运算符的优先级比较。
若高于栈顶元素则进栈,继续扫描下一符号。否则,将运算符栈的栈顶元素退栈,形成一个操作码 Q,同时操作数栈的栈顶元素两次退栈,形成两个操作数 a、b,让计算机对操作数与操作码完成一次运算操作,即 aQb,并将其运算结果存放在操作数栈中。
注意:在向 calc 函数传入表达式之前,必须在表达式两端加一层小括号!

#define Pop { pos--; \
switch (symbol[pos+1]) { \
case '+': number[pos]+=number[pos+1];break; \
case '-': number[pos]-=number[pos+1];break; \
case '*': number[pos]*=number[pos+1];break; \
case '/': number[pos]/=number[pos+1];break; \
case '^': number[pos]=pow(number[pos],number[pos+1]);break;\
}; \
}
#define Push symbol[++pos]=exp[i];
#define SkipBlank { while(c==' ') i++; } // 跳过空白
#define Is_it(x,a,b) (x==a||x==b) // 判断x是否为a或b
#define Is_them(x,a,b,c,d) (x==a||x==b||x==c||x==d) // 判断x是否为a、b、c、d之一
int number[1000]; // 操作数栈
char symbol[1000]; // 运算符栈
int pos;
int pow(int, int); // 乘方运算的代码见37页"4.3 快速幂!"。
int val(char *s, int &i) // 字母变数字
{
int n=0;
while (s[i]>='0' && s[i]<='9')
{
n=n*10+(s[i]-'0');
i++;
}
return n;
}
#define c exp[i]
int calc(char *exp, int n)
{
pos=number[0]=0;
int i=0;
char t;
while (i<=n)
{
SkipBlank
while (c=='(') // 处理左括号
{
SkipBlank
Push
i++;
}
SkipBlank
number[pos]=val(exp,i);
SkipBlank
do
{
if (c==')') // 处理右括号
{
while (symbol[pos]!='(')
Pop
pos--;
number[pos]=number[pos+1];
} else {
while (true) // 运算符入栈或出栈处理
{
if (Is_it(c,'+','-') && symbol[pos]!='(')
Pop
else if (Is_it(c,'*','/') && Is_it(symbol[pos],'*','/'))
Pop
else if (Is_them(c,'+','-','*','/') && symbol[pos]=='^')
Pop
else
break;
}
SkipBlank
Push
}
t=exp[i]; // 接下来可能会遇到空格。如果不这样,会出错的。
i++;
SkipBlank
} while (i<=n && t==')');
}
return number[0];
}

(2) 分治

找出式中最后运算的运算符 A,先递归地求出其左右两边的值,再将得到的值进行 A 运算。

int pow(int, int); // 乘方运算的代码见128页“11.8 快速幂!”。
int str2int(char *s, int x, int y) // 字符串变数字
{
int n=0;
for (int i=x; i<=y; i++)
if (s[i]>='0' && s[i]<='9')
n=n*10+(s[i]-'0');
else if (s[i]!=' ')
break;
return n;
}
int calc(char *exp, int x, int y)
{
int lv=0, p=-1;
// 去除两端空格
while (exp[x]==' ' && x<=y) x++;
while (exp[y]==' ' && x<=y) y--;
for (int i=x; i<=y; i++)
{
char &c = exp[i];
if (c==' ') continue;
if (c=='(') lv++;
if (c==')') lv--;
if (lv==0)
{
// 当出现优先级运算时,采取优先级较低的运算符;优先级相同时,采取靠后的运算符。
if (Is_it(c,'+','-'))
p=i;
else if (Is_it(c,'*','/') && (p==-1 || !Is_it(exp[p],'+','-')))
p=i;
else if (p==-1 && c=='^')
p=i;
}
}
if (p==-1)
{
// 去除两端括号
if (exp[x]=='(' && exp[y]==')') return calc(exp, x+1, y-1);
if (exp[x]=='(') return calc(exp, x+1, y);
if (exp[y]==')') return calc(exp, x, y-1);
// 字符变数字
return str2int(exp, x, y);
} else {
int a=calc(exp, x, p-1);
int b=calc(exp, p+1, y);
switch (exp[p])
{
case '+': return a+b;
case '-': return a-b;
case '*': return a*b;
case '/': return a/b;
case '^': return pow(a,b);
};
}
return 0;
}

(3) 表达式树
用分治和递归的思想构建表达式树。对树进行一次遍历,边遍历边计算,就可以算出表达式的值。
对表达式树进行前序遍历、中序遍历和后序遍历,分别会得到前缀表达式、中缀表达式和后缀表达式。其中后缀表达式不需要括号。以下图为例,三种表达式分别为:
前缀表达式:- + 3 * 7 - 3 2 / 4 2

中缀表达式:3 + 7 * 3 - 2 - 4 / 2

后缀表达式:3 7 3 2 - * + 4 / 2 –

 

相关文章:

  • 09-排序3 Insertion or Heap Sort(浙大数据结构)
  • java-python+vue社区防疫服务管理系统网站
  • sparksql insertinto 源码解析
  • 反射之获取Class
  • 【博客477】prometheus-----数值数据编码(varint与zigzag)
  • LCMXO2-2000HC-4FTG256C FPGA MachXO2系列 256-FTBGA 现场可编程门阵列
  • 初始Cpp之 四、数据类型
  • office2019如何自定义安装位置?
  • java基于ssm的汽车维修保养管理系统
  • Std::optional 源码分析
  • 目标检测——关键点检测学习记录(四):人脸和手部特征点检测
  • IMX6ULL学习笔记(5)——获取和编译U-Boot
  • 尚硅谷Vue系列教程学习笔记(14)
  • linux虚拟机mysql
  • golang基于errgroup实现并发调用
  • 【译】理解JavaScript:new 关键字
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • 2017前端实习生面试总结
  • 345-反转字符串中的元音字母
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Android单元测试 - 几个重要问题
  • Angular js 常用指令ng-if、ng-class、ng-option、ng-value、ng-click是如何使用的?
  • angular2开源库收集
  • Debian下无root权限使用Python访问Oracle
  • Idea+maven+scala构建包并在spark on yarn 运行
  • javascript面向对象之创建对象
  • MySQL几个简单SQL的优化
  • pdf文件如何在线转换为jpg图片
  • Spring Boot MyBatis配置多种数据库
  • Unix命令
  • VUE es6技巧写法(持续更新中~~~)
  • vue 配置sass、scss全局变量
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • web标准化(下)
  • 测试如何在敏捷团队中工作?
  • 从PHP迁移至Golang - 基础篇
  • 高性能JavaScript阅读简记(三)
  • 欢迎参加第二届中国游戏开发者大会
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 移动端 h5开发相关内容总结(三)
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • #### go map 底层结构 ####
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (Git) gitignore基础使用
  • (js)循环条件满足时终止循环
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (黑马C++)L06 重载与继承
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目