【数据结构】详解栈的应用之表达式求值

2023-11-19

首先明白:

前缀表达式:符号在前,如-×+3456

中缀表达式:符号在中间,如(3 + 4) × 5 - 6

后缀表达式:符号在最后,如34+5×6-,后缀表达式不出现括号。

中缀表达式转后缀表达式的方法: 


1.遇到数字:直接输出(添加到后缀表达式中)
2.栈为空时,遇到运算符,直接入栈
3.遇到左括号:将其入栈
4.遇到右括号:执行出栈操作,将栈的元素全部输出,直到弹出栈的是左括
号为止,且左括号不输出直接删除
5.遇到其他运算符:加减乘除:若当前运算符小于等于栈中运算符优先级,依次出栈,然后再将该运算符入栈
6.最终将栈中的符号依次出栈,输出。


运算符优先级(常用的记忆一下,和常识吻合)
1 [] () (同级)
3 */
4 +-

 代码

//定义一个运算符栈op
struct{
	char data[MAXSIZE]; //存放运算符
	int top;  //栈顶指针 
}op; 
void trans(char exp[],char postexp[]){
	char ch;
	int i,j;    //i是中缀exp的下标,j是后缀postexp的下标
	op.top=-1;
	ch=exp[i];  //获取输入的第一个字符 
	i++;
	while(ch!='\0'){
		switch(ch){
			case '(':    //遇到左括号直接入栈 
				op.top++;
				op.data[op.top]=ch;
				break;
			case ')':    //遇到右括号,栈顶元素不断出栈直到遇到左括号 
				while(op.data[op.top]!='('){
					postexp[j]=op.data[op.top];
					j++;
					op.top--;
				}
				op.top--;  //遇到了左括号,直接删除 
				break; 
			case '+':   //因为加减运算符优先级最低,不大于任何其他运算符,所以直接入栈 
			case '-':
				while(op.top!=-1&&op.data[op.top]!='('){ //栈不为空且没有遇到左括号,都直接出栈 
					postexp[j]=op.data[op.top];
					j++;
					op.top--;
				}
				op.top++;         //即:栈为空或遇到了左括号,当前运算符进栈 
				op.data[op.top]=ch;
				break;
			case '*':
			case '/':
				while(op.top!=-1&&op.data[op.top]!='('&&(op.data[op.top]=='*'||op.data[op.top]=='/')){
					postexp[j]=op.data[op.top];  //栈不为空且没有遇到左括号且遇到了同级符号:都出栈 
					j++;
					op.top--;
				}
				op.top++;   //不满足上述时,入栈 
				op.data[op.top]=ch;
				break;
			case ' ':break;   //过滤空格
			//default一般用在最后,表示非以上的任何情况下而发生的情况,
			default:
				while(ch>='0'&&ch<='9'){ //输入的是数字 
				    postexp[j]=ch;   //直接入栈 
				    j++;
			 	    ch=exp[i];  //遍历下一个数字 
			 	    i++;
			    } 
			    i--;
			    postexp[j]='#';  //用#标识一个数值串结束 
		 	    j++;
			break;       
		}
		ch=exp[i];
		i++;
	}
	while(op.top!=-1){   //此时exp扫描完毕,栈不空时出栈并存放到postexp中 
		postexp[j]=op.data[op.top];
		j++;
		op.top--;
	}
	postexp[j]='\0';   //添加结束标识,后缀表达式输入完毕 
}

转换的另一种方法:手工加括号

例如:5+2*(1+6)-(8/2)

1)  先按照运算符的优先级对中缀表达式加括号,

     先计算2*(1+6),加上括号,得到5+(2*(1+6))-8/2

     再计算除法,得到5+(2*(1+6))-(8/2)

     再计算加法,得到(5+(2*(1+6)))-(8/2)

     最后进行减法,得到((5+(2*(1+6)))-(8/2))

2)  将每一个右括号替换成其离得最近的符号

     得到 ( ( 5 ( 2 ( 1 6 + * + (8 2 / -

3)  去除所有的左括号 ,得到 5 2 1 6 + * + 8 2 / -

该方法不能用程序实现

前缀表达式求值

  1. 从右至左扫描表达式
  2. 遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算,并将结果入栈
  3. 重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果

示例:
计算前缀表达式的值:- + 1 × + 2 3 4 5

  1. 从右至左扫描,将5,4,3,2压入堆栈;
    2)遇到+运算符,弹出2和3(2为栈顶元素,3为次顶元素),计算2+3的值,得到5,将5压入栈;
    3)遇到×运算符,弹出5和4,计算5×4的值,得到20,将20压入栈;
    4)遇到1,将1压入栈;
    5)遇到+运算符,弹出1和20,计算1+20的值,得到21,将21压入栈;
    6)遇到-运算符,弹出21和5,计算21-5的值,得到16为最终结果

可以看到,用计算机计算前缀表达式是非常容易的,不像计算后缀表达式需要使用正则匹配

后缀表达式求值

与前缀表达式类似,只是顺序是从左至右:

  1. 从左至右扫描表达式
  2. 遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算,并将结果入栈
  3. 重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果

示例:
计算后缀表达式的值:1 2 3 + 4 × + 5 -

1)从左至右扫描,将1,2,3压入栈;
2)遇到+运算符,3和2弹出,计算2+3的值,得到5,将5压入栈;
3)遇到4,将4压入栈
4)遇到×运算符,弹出4和5,计算5×4的值,得到20,将20压入栈;
5)遇到+运算符,弹出20和1,计算1+20的值,得到21,将21压入栈;
6)遇到5,将5压入栈;
7)遇到-运算符,弹出5和21,计算21-5的值,得到16为最终结果

代码 

struct{
	float data[MAXSIZE];
	int top;   //栈顶指针 
};
float compvalue(char postexp[]){
	float d;
	char ch;
	int i=0;   //i作为postexp下标 
	st.top=-1;
	ch=postexp[i];
	i++;
	while(ch!='\0'){
		switch(ch){
			case '+':
				st.data[st.top-1]=st.data[st.top-1]+st.data[st.top];
				st.top--;
				break;
			case '-':
				st.data[st.top-1]=st.data[st.top-1]-st.data[st.top];
				st.top--;
				break;
			case '*':
				st.data[st.top-1]=st.data[st.top-1]*st.data[st.top];
				st.top--;
				break;
			case '/':
				if(st.data[st.top]!=0){
					st.data[st.top-1]=st.data[st.top-1]/st.data[st.top];
				}else{
					printf("除零错误!\n");
					exit(0);  //异常退出 
				}
				st.top--;
				break;
			default:
				d=0;
				while(ch>='0'&&ch<='9'){
					d=d*10+ch-'0';   //将数字字符转换成对应的数字存入d 
					ch=postexp[i];
					i++;
				}
				st.top++;
				st.data[st.top]=d;
		}
		ch=postexp[i];
		i++;
	}
	return st.data[st.top];
}

测试 

#define MAXSIZE 100
#include <stdio.h>
int main()
{
	int i = 0;
	char exp[] = {"(2*2)*1+3*2/1"};
	char postexp[MAXSIZE];
	trans(exp,postexp);
	while (postexp[i] != '\0')
	{
		printf("%c",postexp[i]);
		i++;
	}
	printf("\n");
	printf("运算结果为:%f.\n", compvalue(postexp));

	return 0;
}


中缀表达式求值

口诀:

操作数直入左栈
运算符入栈有规则
若遇栈空直接入栈
若栈不空看优先级
栈顶元素优先级只有大于等于扫描元素的优先级才出栈
否则遇到都入栈
左括号入栈直到遇到右括号吐出来

中缀表达式求值过程:

例:求5+7*3*(2+1)

(1)准备两个空栈,数字入左栈,符号入右栈

(2)5进左栈,+进右栈

(3)7进左栈,*的优先级大于+,直接进右栈

(4)3进左栈

(5)*的优先级等于*,第一个*出栈,并且左栈要弹出两个元素进行运算,即3*7=21,并将新的运算结果压入左栈

(6)第二个*进右栈,左括号(直接进栈

(7)2进左栈

(8)+直接进右栈

(9)1进左栈

 

(10)遇到右括号,之前的元素全部弹出进行运算直到遇见左括号

(11)1+2=3,将3压入左栈,(左括号删除

(12)21*3=63,将63压入左栈

(13)63+5=68,最后的左栈元素为68

总结

  • 前缀表达式和后缀表达式计算的时候都是从一个方向扫描表达式,遇到数字压入栈,遇到运算符弹出栈顶的两个数进行运算并将结果入栈,重复知道结束
  • 前缀和后缀表达式已经内在地包含运算顺序,因此不用括号来确定优先级
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【数据结构】详解栈的应用之表达式求值 的相关文章

  • Android 旧项目引入 (kotlin)插件简单记录

    1 确认自己 AS 的kotlin 插件 已经安装 2 选择kotlin plugin updates 文件显示 3 选择configure kotlin in project 进入选择 Android gradle 的选项 由于我这边配置
  • JAVA中数组冒泡排序和选择排序

    冒泡排序的思想 两两之间比较大小 小的数在前 大的数在后 共比较i 1次 static void MaoPaoArray int a for int i 0 i lt a length 2 i for int j 0 j lt a leng
  • tortoisegit 常见错误disconnected no supported authentication methods available(server sent: publickey)

    本文转载自 https blog csdn net yym6789 article details 53807640 1 安装好小乌龟git后 用小乌龟的pull 从github上拉取项目 遇到错误 disconnected no supp
  • 7 整数反转 c++

    leetcode7 整数反转 题目描述 给出一个 32 位的有符号整数 你需要将这个整数中每位上的数字进行反转 注意 如果反转后整数溢出那么就返回 0 算法思路 使用一个64位的long long类型来存储结果整数 避免反转后结果溢出报错
  • C++ 装饰器模式

    什么是装饰器模式 装饰器模式是一种结构型设计模式 实现了在不改变现有对象结构的的同时又拓展了新的功能 装饰器本质上是对现有对象的重新包装 同时装饰器又称为封装器 如何理解装饰器模式 以笔记本电脑为例 当我们购买了一台新笔记本电脑 但我们发现

随机推荐

  • 在openwrt使用C语言增加ubus接口(包含C uci操作)

    在openwrt使用C语言增加ubus接口 包含C uci操作 文章目录 在openwrt使用C语言增加ubus接口 包含C uci操作 创建自己的软件包 软件包结构 编写代码和启动脚本 重点 案例大致分析 实现过程 ubus demo i
  • java调用脚本语言笔记(jython,jruby,groovy)

    java调用脚本语言笔记 jython jruby groovy 有两种方法 1 java se 6以后实现了jsr 223规范 java代码 java view plain copy ScriptEngineManager factory
  • C语言:深度解析各种数据在计算机内存中的存储

    文章目录 数据的各种类型的存储 各种数据类型的意义是什么 整形在计算机内存中的存储 原码反码补码 为什么数据的存放都是补码 什么是大小端 整形提升 什么是整型提升 那么整型提升是做什么的 如何进行整形提升 关于整型提升的例子 关于整形数据存
  • c++编写COM组件,并使用该组件

    在网上看了很多个介绍com组件的方法 对于一个新手来说看很久都看不懂 自己项目需要实现com 于是自己整理了一个文档和代码 先记录下来 以防以后用的上 步骤如下 1 新建ATL项目 你也可以是其他项目 只要是dll就行 可以支持MFC AT
  • [Excel VBA]如何拷贝数组?

    本文翻译至 http itpro nikkeibp co jp atcl column 15 090100207 090100143 ST system Variant型变量 数组 数组是可以保存多个值的 一种变量 变量是独幢楼房的话 数组
  • 详解java设计模式之-----观察者模式

    观察者模式 对象间的联动 什么是观察者模式呢 首先 看一个故事 红灯停 绿灯行 在日常生活中 交通信号灯装点着我们的城市 指挥着日益拥挤的城市交 通 当红灯亮起 来往的汽车将停止 而绿灯亮起 汽车可以继续前行 在这个过程中 交 通信号灯是汽
  • 串行接口的工作原理和实现

    串口的结构和工作原理 通用异步收发传输器 Universal Asynchronous Receiver Transmitter 通常称作UART 它将要传输的资料在串行通信与并行通信之间加以转换 作为把并行输入信号转成串行输出信号的芯片
  • postman接口测试的关联测试

    在接口测试中 很多时候需要依赖前一个请求的响应数据关联到后一个请求的请求数据中来 在postman的中有一个Pre request Script 板块 如示例接口为 https api weixin qq com cgi bin user
  • Java面向对象基础

    文章目录 面向对象 一 类和对象 1 类的介绍 2 类和对象的关系 3 类的组成 4 创建对象和使用对象的格式 二 对象内存图 1 单个对象内存图 2 两个对象内存图 3 两个引用指向相同内存图 三 成员变量和局部变量 四 this 关键字
  • 栈(Stack)——class Stack 和 class Stack T 实现

    对于Stack类的实现 跟之前链表实现也一样 只是封装成为面向对象的类了 PS 这里是线式存储的类和模板实现 链表式的实际上写法也是一样的 class Stack代码如下 mystack h include
  • 信奥一本通 贪心算法 回顾

    文章目录 写在前面 A 家庭作业 B 智力大冲浪 C 加工生产调度 D 喷水装置3 线段覆盖最少线段 E 活动安排 线段覆盖 覆盖最多段 F 种树 G 数列极差 H 数列分段 I 钓鱼 J 均分纸牌 K 糖果传递 写在前面 之前看到一篇非常
  • java中的双端队列deque使用以及部分原理

    转载自 https www cnblogs com denglh p 7911513 html package collections import java util Deque import java util LinkedList P
  • 对于金融机构而言,为什么选择私有化 IM 比企业微信、钉钉更好?

    一 金融机构数字化转型迈向规范有序 更成体系的新阶段 当前 新一轮信息技术革命浪潮拉开序幕 以人工智能 大数据 云计算等为代表的数字技术正在重构全球经济 不少企业也纷纷拥抱数字化浪潮 开展全方位的变革和升级 中国银保监会印发 关于银行业保险
  • char字符表

    图片来源于网上
  • 如何在 GitHub 上找到免费且实用的软件?

    GitHub 虽说是以程序员为主的社区 但是上面托管的项目类型却风格迥异 有认真科研型的 也有上班划水型的 有面向极客宅男的开发工具 也有给小白麻瓜使用的普通软件 本周写了几篇文章 大多都在介绍与技术相关的开发工具与技巧 今天稍微调整一下
  • 生成csv文件并下载

    在做项目中 我们做一个功能的时候 可能要把数据做导出或下载处理 下载成各种格式 下面提供了一种excel下载格式 csv 将得到的数据 经过处理生成csv文件 并激活下载到本地 代码如下
  • 云计算目前国内外发展现状是什么,云计算主要存在哪些问题?

    远在 云计算 的名次出现之前 我国相关科技人员便已对互联网的透明资源储备技术进行了多方面应用 而随着科技的不断进步 对于云计算的应用愈加频繁 政府部门对云计算的建设提供了经济基础与社会软环境的保障 并且在国家科研部门当中设立了专业的部门 直
  • Android 获取本地视频列表

    activity video list xml
  • 将二叉树转换成双向链表

    输入一棵二叉搜索树 将该二叉搜索树转换成一个排序的双向链表 要求不能创建任何新的节点 只能调整树中节点指针的指向 因为二叉搜索树每个节点都有两个指针 分别是指向其左子节点和右子节点 所以将该节点的左子节点变成双向链表中的左节点 将该节点的右
  • 【数据结构】详解栈的应用之表达式求值

    首先明白 前缀表达式 符号在前 如 3456 中缀表达式 符号在中间 如 3 4 5 6 后缀表达式 符号在最后 如34 5 6 后缀表达式不出现括号 中缀表达式转后缀表达式的方法 1 遇到数字 直接输出 添加到后缀表达式中 2 栈为空时