深入理解算数表达式求值:后缀表达式的转换与计算

news2024/9/24 0:11:13

归纳编程学习的感悟,
记录奋斗路上的点滴,
希望能帮到一样刻苦的你!
如有不足欢迎指正!
共同学习交流!
🌎欢迎各位→点赞 👍+ 收藏⭐ + 留言​📝

没人会嘲笑竭尽全力的人!

前言

在计算机科学中,算数表达式的求值是一个经典的问题,尤其是在编译器设计和计算器应用中。中缀表达式,即我们日常所见的数学表达式(如 3 + 4(2 + 3) * 4),虽然直观易懂,但对于计算机来说并不友好,因为它们需要遵循特定的运算优先级规则。因此,将中缀表达式转换为后缀表达式(也称为逆波兰记法或RPN)可以简化计算过程,使计算机能够更有效地进行求值。

后缀表达式简介

后缀表达式的特点是运算符紧跟在其操作数之后,例如,中缀表达式 3 + 4 转换为后缀表达式后成为 3 4 +。这种格式消除了对运算符优先级的依赖,也不需要使用括号来表示运算的顺序,这使得计算变得更为直接和高效。

中缀到后缀的转换

步骤概述
  1. 初始化:创建一个空的运算符栈和一个空的数组。
  2. 遍历输入:逐个检查中缀表达式中的每个字符。
    • 如果是操作数,直接将其添加到数组。
    • 如果是运算符,根据栈顶运算符的优先级与当前运算符比较:
      • 若当前运算符优先级高于栈顶运算符,则将当前运算符压入栈。
      • 若当前运算符优先级低于或等于栈顶运算符,从栈中弹出运算符并添加到数组,直到栈顶运算符优先级低于当前运算符,再将当前运算符压入栈。
    • 如果是左括号 (,直接压入栈。
    • 如果是右括号 ),则不断从栈中弹出运算符并添加到数组,直到遇到对应的左括号,然后将这对括号丢弃。
  3. 处理剩余运算符:当所有字符被处理后,将栈中剩余的运算符依次弹出并添加到数组。

后缀表达式的计算

计算后缀表达式相对直接,主要步骤如下:

  1. 初始化:创建一个空的数值栈。
  2. 遍历后缀表达式:逐个检查后缀表达式中的每个元素。
    • 如果是操作数,将其压入数值栈。
    • 如果是运算符,从数值栈中弹出两个操作数,执行相应的运算,然后将结果压回栈中。
  3. 最终结果:当所有元素被处理后,数值栈的顶部元素就是整个表达式的计算结果。

示例

假设我们要计算中缀表达式 (3 + 4) * 5 的值,我们首先将其转换为后缀表达式:

  1. 初始化:[](运算符栈) [](输出队列)
  2. 遍历:
    • (: 压栈 [(]
    • 3: 输出 [3]
    • +: 压栈 [( +]
    • 4: 输出 [3 4]
    • ): 弹出 + 并输出 [3 4 +]
    • *: 压栈 [ *]
    • 5: 输出 [3 4 + 5]
    • EOL: 弹出 * 并输出 [3 4 + 5 *]
  3. 结果:3 4 + 5 *

接下来,我们计算这个后缀表达式:

  1. 初始化:[](数值栈)
  2. 遍历:
    • 3: 压栈 [3]
    • 4: 压栈 [3 4]
    • +: 弹出 4 和 3,计算 3 + 4 = 7,压栈 [7]
    • 5: 压栈 [7 5]
    • *: 弹出 5 和 7,计算 7 * 5 = 35,压栈 [35]
  3. 结果:35

因此,中缀表达式 (3 + 4) * 5 的值为 35

结论

后缀表达式的转换和计算提供了一种高效且无歧义的方式来处理算数表达式。这种方法不仅在理论上有趣,在实际应用中也非常实用,特别是在需要快速准确地计算表达式的场景中。

下面是我的代码部分

中缀转后缀利用的链栈,所以先加入链栈头文件。

LinkStack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>

typedef char DataType;

typedef struct LStackNode
{
	DataType data;
	struct LStackNode* next;
}LStackNode,*LinkStack;

void InitLinkStack(LinkStack* top);

int StackEmpty(LinkStack top);

int PushStack(LinkStack top, DataType e);

int PopStack(LinkStack top, DataType* e);

int GetTop(LinkStack top, DataType* e);

int StackLength(LinkStack top);

void DestoryStack(LinkStack top);

LinkStack.cpp

#include "LinkStack.h"
#define _CRT_SECURE_NO_WARNINGS 1

void InitLinkStack(LinkStack* top)
{
	*top = (LinkStack)malloc(sizeof(LStackNode));
	(*top)->next = NULL;//将链栈头结点指针域置为空
}

int StackEmpty(LinkStack top)
{//判断链栈是否为空
	if (top->next == NULL)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

int PushStack(LinkStack top, DataType e)
{//将元素e入栈,入栈成功返回1
	LStackNode* p;
	p = (LStackNode*)malloc(sizeof(LStackNode));
	p->data = e;
	p->next = top->next;
	top->next = p;
	return 1;
}

int PopStack(LinkStack top, DataType* e)
{//将栈顶元素出栈
	LStackNode* p;
	p = top->next;
	if (!p)
	{
		printf("栈已空\n");
		return 0;
	}
	*e = p->data;
	top->next = p->next;
	free(p);
	return 1;
}

int GetTop(LinkStack top, DataType* e)
{//取栈顶元素
	LStackNode* p;
	p = top->next;
	if (!p)
	{
		printf("栈已空\n");
		return 0;
	}
	*e = p->data;
	return 1;
}

int StackLength(LinkStack top)
{//求链栈长度
	LStackNode* p;
	int count = 0;
	p = top;
	while (p->next)
	{
		p = p->next;
		count++;
	}
	return count;
}

void DestoryStack(LinkStack top)
{//销毁链栈
	LStackNode* p, * q;
	p = top;
	while (!p)
	{
		q = p;
		p = p->next;
		free(q);
	}
}

主函数

#define _CRT_SECURE_NO_WARNINGS 1
#include"LinkStack.h"
#include<stdio.h>

constexpr auto MaxSize = 50;;

typedef struct
{
	float data[MaxSize];
	int top;
}SeqStack;

void TranslateExpress(char str[], char exp[]);
float ComputeExpress(char exp[]);

int main()
{
	char a[MaxSize], b[MaxSize];
	float f;
	printf("请输入一个算数表达式:\n");
	gets_s(a);
	printf("中缀表达式为:%s\n", a);
	TranslateExpress(a,b);
	printf("后缀表达式为:%s\n",b);
	f = ComputeExpress(b);
	printf("计算结果:%.0f\n", f);
	return 0;
}

void TranslateExpress(char str[], char exp[])
{//中缀表达式转后缀表达式
	LinkStack S;//定义一个栈,用于存放运算符
	InitLinkStack(&S);
	char ch;
	char e;
	int i = 0, j = 0;
	ch = str[i];
	i++;
	while (ch != '\0')//依次扫描中缀表达式中的每个字符
	{
		switch (ch)
		{
		case '('://如果当前字符是左括号,则将其入栈
			PushStack(S, ch);
			break;
		case ')'://如果是右括号,则将栈中的运算符出栈,并将其存入数组exp中
			while (GetTop(S, &e) && e != '(')
			{
				PopStack(S, &e);
				exp[j] = e;
				j++;
				exp[j] = ' ';
				j++;
			}
			PopStack(S, &e);//将左括号出栈
			break;
		case '+'://如果遇到+和-,因为其优先级低于栈顶运算符的优先级,
		case '-'://所以先将栈顶运算符出栈,并将其存入数组exp中,然后将当前运算符入栈
			while (!StackEmpty(S) && GetTop(S, &e) && e != '(')
			{
				PopStack(S, &e);
				exp[j] = e;
				j++;
				exp[j] = ' ';
				j++;
			}
			PushStack(S, ch);//将当前运算符入栈
			break;
		case '*'://如果遇到的是*和/,则先将同级运算符出栈,并存入数组exp中,
		case '/'://然后将当前运算符出栈
			while (!StackEmpty(S) && GetTop(S, &e) && e == '/' || e == '*')
			{
				PopStack(S, &e);
				exp[j] = e;
				j++;
				exp[j] = ' ';
				j++;
			}
			PushStack(S, ch);//当前运算符入栈
			break;
		case ' '://如果遇到空格,忽略
			break;
		default://如果遇到操作数,则将操作数直接送入数组exp中,并在
			    //其后添加一个空格,用来分割数字字符
			while (ch >= '0' && ch <= '9')
			{
				exp[j] = ch;
				j++;
				ch = str[i];
				i++;
			}
			i--;
			exp[j] = ' ';
			j++;
			break;
		}
		ch = str[i];//读入下一个字符,准备处理
		i++;
	}
	while (!StackEmpty(S))//将栈中所有剩余的运算符出栈,送入数组exp中
	{
		PopStack(S, &e);
		exp[j] = e;
		j++;
		exp[j] = ' ';
		j++;
	}
	exp[j]='\0';
}



float ComputeExpress(char exp[])
{//计算后缀表达式的值
	SeqStack S;//利用顺序栈存储浮点型数据,从而防止与链栈中存储的
	S.top = -1;//字符数据冲突,利用C++中的模板能更好的解决这一点
	int i = 0, value;
	float x1, x2;
	float result;
	while (exp[i] != '\0')
	{
		if (exp[i] != ' ' && exp[i] >= '0' && exp[i] <= '9')
		{//如果当前字符是数字字符
			value = 0;
			while (exp[i] != ' ')
			{
				value = value * 10 + exp[i] - '0';
				i++;
			}
			S.top++;
			S.data[S.top] = value;//处理之后将数字入栈
		}
		else//如果当前字符是运算符
		{
			switch (exp[i])
			{//将栈中的数字出栈两次,然后用当前的运算符进行运算,再将结果入栈
			case '+':
				x1 = S.data[S.top];
				S.top--;
				x2 = S.data[S.top];
				S.top--;
				result = x2 + x1;
				S.top++;
				S.data[S.top] = result;
				break;
			case '-':
				x1 = S.data[S.top];
				S.top--;
				x2 = S.data[S.top];
				S.top--;
				result = x2 - x1;
				S.top++;
				S.data[S.top] = result;
				break;
			case '*':
				x1 = S.data[S.top];
				S.top--;
				x2 = S.data[S.top];
				S.top--;
				result = x2 * x1;
				S.top++;
				S.data[S.top] = result;
				break;
			case '/':
				x1 = S.data[S.top];
				S.top--;
				x2 = S.data[S.top];
				S.top--;
				result = x2 / x1;
				S.top++;
				S.data[S.top] = result;
				break;
			}
			i++;
		}
	}
	result = S.data[S.top];
	S.top--;
	if (S.top == -1)//如果栈不为空,则将结果出栈,并返回
	{
		return result;
	}
	else
	{
		printf("表达式错误\n");
		exit(-1);
	}
}

反思总结:

        当时写的时候看着参考书的代码一直有个疑问,为什么中缀转后缀用的链栈,而计算后缀的时候用的顺序栈,为什么不能直接都用链栈呢,通过一番折腾,我知道了,因为我的参考书是一本C语言的数据结构,在LinkStack.h里面我规定了DataType是char类型,中缀转后缀,链栈里面存储的是char类型,但是要是计算后缀的话就不能再调用链栈这个数据结构了,因为计算后缀要将数值入栈,是float类型,此时由于我写的链栈只能存储char类型而导致编译器报错,解决这个问题我查阅资料找到了多种办法:一,由于C语言的限制,我可以再写一个存储数值的链栈数据结构,这样较麻烦,得重写链栈操作文件;二,可以利用指针函数,这个又要大幅度改动我的LinkStack.cpp文件里面的函数传参部分,我也没选这种方法;三,可以在main函数里面利用另一种数据结构顺序栈,这是我觉得更方便的办法,虽然会导致部分内存空间的浪费,但只需要简单的改动栈顶指针进行入栈和出栈操作即可。

        另外,如果是C++语言将更好的解决这一问题,C++引入了模板,可以在一个程序里利用存储不同数据类型的数据结构,我对模板的应用不太深入,C++是大一上学期自学的,现在已经忘了大部分,所以返回去深入学习的一下模板的应用,针对本题在C++中直接定义LinkStack<char> charStack和LinkStack<float> floatStack可以完美的解决这一问题。

        希望这篇博客能帮助你更好地理解后缀表达式的转换和计算过程,以及它在算数表达式求值中的重要性。如果你有任何疑问或需要进一步的解释,请随时提问!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1946098.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件渗透测试包括的内容和作用简析

在当今信息技术迅速发展的时代&#xff0c;软件安全已成为企业和用户关注的重中之重。尤其是渗透测试作为一种专业的安全测试方法&#xff0c;日益受到企业们的重视。   软件渗透测试是一种模拟恶意攻击者的方式&#xff0c;对软件及其相关系统进行评估&#xff0c;以发现可能…

html基础及python web开发

1.标签 ①p标签 段落标签&#xff0c;分隔段与段。 <p>...</p> ②h标签 标题标签h1-6&#xff0c;大小依次减小 <h1>...</h1> <h2>...</h2> <h3>...</h3> <h4>...</h4> <h5>...</h5> <h6>.…

数据结构的概念和术语

目录 一.前言 二.数据结构的基本概念 三.数据结构的术语 一.前言 数据结构是一门研究非数值计算的程序设计中计算机的操作对象以及它们之间的关系和操作的学科。数据结构的基本数据结构包括两部分&#xff0c;线性结构跟非线性结构。 二.数据结构的基本概念 数据结构主要包括…

MarkTool集合篇

MarkTool目前包含以下几种工具 1、TCP客户端 2、TCP服务端 3、UDP客户端 4、Web客户端 5、Web服务端 6、串口网口 7、PLC 8、获取本机设备 9、Log 10、密钥 11、系统设置 11-1、基本设置 11-2、角色设置 11-3、用户设置 11-4、log记录 开启软件需要找我解密&#…

怎样做好仓库管理工作?如何利用仓库管理系统进行有效管理?

我前前后后跑遍了十几家仓储设备公司&#xff0c;跟那些制造业的朋友们聊了个痛快&#xff0c;从他们那儿学到了不少仓库管理的实践方法。 回来自己整理了一套仓库管理更高效的实用方法&#xff0c;现在就来跟大家伙儿聊聊仓库管理中那些常见问题&#xff0c;以及我是怎么琢磨…

(前缀和) LeetCode 238. 除自身以外数组的乘积

一. 题目描述 原题链接 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&…

HarmonyOS网络请求的简单用法,HttpUtil简单封装

请求网络获取数据 点击按钮发送一个post请求&#xff0c;发送一条string由于此处的返回result.data本身就是一个string&#xff0c;因此不需要转换类型 Button(请求网络).margin({ top: 10 }).fontSize(24).fontWeight(FontWeight.Bold).onClick(() > {httpRequestPost(http…

算法从零到精通 (一) ~ 快慢双指针

1. 前言 快慢双指针是一种常用的算法技巧&#xff0c;通常用于解决涉及链表或数组的问题。它的基本思想是使用两个指针&#xff0c;一个移动速度快&#xff08;快指针&#xff09;&#xff0c;一个移动速度慢&#xff08;慢指针&#xff09;&#xff0c;来解决特定的问题。这两…

【高可用】利用AOP实现数据库读写分离

最近项目中需要做【高可用】数据库读写分离相关的需求&#xff0c;特地整理了下关于读写分离的相关知识。项目中采用4台数据库&#xff1a;1个master&#xff0c;2个slave&#xff0c;1个readOnly&#xff0c;其中master数据库会自动定时同步到readOnly节点。可以通过中间件(Sh…

Mysql —— 事务

目录 什么是事务&#xff1f; 两种方式实现事务&#xff1a; 方法一 方法二&#xff1a; 事务四大特性(简称ACID) 并发事务问题&#xff08;面试题&#xff09; 事务隔离级别 什么是事务&#xff1f; 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff…

跨域浏览器解决前端跨域问题

1.问题背景 这是一种属于非主流的解决跨域的方案&#xff0c;但是也是可以正常使用而且比较简单的。如果需要使用主流的解决前端跨域方案&#xff0c;请参考这篇文章。 我这边其实是优先建议大家使用主流的跨域方案&#xff0c;如果主流的实在不行&#xff0c;那么就使用跨域…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 图像物体的边界(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

scratch绘制十个圆 2024年6月中国电子学会图形化编程 少儿编程 scratch编程等级考试三级真题和答案解析

目录 scratch绘制十个圆 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、实现流程 1、案例分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、…

css气泡背景特效

css气泡背景特效https://www.bootstrapmb.com/item/14879 要创建一个CSS气泡背景特效&#xff0c;你可以使用CSS的伪元素&#xff08;:before 和 :after&#xff09;、border-radius 属性来创建圆形或椭圆形的“气泡”&#xff0c;以及background 和 animation 属性来设置背景…

mysql 先子查询过滤再联合 和 先联合再过滤 执行效率问题

执行顺序 先子查询过滤再联合 SELECT XXX FROM(select * from edw_data_dyd.overrun_offsite_info WHERELENGTH( VEHICLE_ID ) > 12 AND CREATED_TIME > DATE_ADD(NOW(),INTERVAL -1 HOUR)AND CREATED_TIME < NOW()AND VEHICLE_ID not like %无车牌%AND VEHICLE_I…

华为ICT大赛之ensp软件BGP原理与配置

BGP基础 1.用于不同自治系统AS(autonomous system)之间动态交换路由信息&#xff1b; BGP取代EGP(exterior gateway protocol)外部网关协议&#xff0c;BGP在其发布路由信息基础上可以进行路由优选&#xff0c;高效处理路由信息&#xff1b; AS:同一组织管理下&#xff0c;使…

全面解析 SnowNLP:中文文本处理、情感分析

1 前言 SnowNLP 是一个专门用于处理中文文本的 Python库。功能包括&#xff1a; 分词情感分析关键词提取文本分类拼音转换繁体转简体词相似度计算等 snownlp0.12.3测试环境&#xff1a;Python3.10.9 2 分词 中文分词&#xff08;Character-Based Generative Model&#xf…

WEB渗透Web突破篇-前端突破

逻辑漏洞 1.任意用户注册 2.用户名枚举 3.爆破用户名&#xff0c;密码 4.用户名注入 5.万能密码 6.用户名Xss 7.修改返回包信息&#xff0c;登入他人账户 8.修改cookie中的参数&#xff0c;如user,adminid等 9.HTML源码、JS等查看信息搜集那一章 10.后台登录参数修改…

MSPM0G3507学习笔记1:开发环境_引脚认识与点灯

今日速通一款Ti的单片机用于电赛&#xff1a;MSPM0G3507 这里默认已经安装好了Keil5_MDK 首先声明一下: 因为是速成&#xff0c;所以需要一定单片机学习基础&#xff0c;然后我写的也不会详细&#xff0c;这个专栏的笔记也就是自己能看懂就行的目标~~~ 文章提供测试代码解…

Go: IM系统分布式架构方案 (6)

分布式部署可能遇到的问题 常见 nginx 反向代理方案 假设按照上述架构方案来A用户接入后connA(ws客户端) 由节点1来维护B用户接入后connA(ws客户端) 由节点2来维护流程: A->B 发信息: A -> connA -> 分析处理 -> connB -> B实际上&#xff0c;上述流程是没有办法…