数据结构3、基于栈的后缀算术表达式求值

1 题目描述

图1 中缀表达式转化为后缀表达式题目描述
图2 基于栈的后缀算术表达式求值题目描述

2 题目解读

        借助一个运算符栈,可将中缀表达式转化为后缀表达式;借助一个运算数栈,可对后缀表达式求值。借助一个运算符栈和一个运算数栈,则可将中缀表达式转化为后缀表达式输出,并根据后缀表达式计算。

3 小题一:中缀表达式转化为后缀表达式

        借助一个运算符栈,则可以将中缀表达式转化为后缀表达式。

3.1 解题思路

        为实现中缀表达式转换成后缀表达式,可以使用一个工作栈OPTR寄存运算符,初始化为空栈;使用一个字符串Postfix寄存转换得到的后缀表达式,初始化为空串。具体步骤如下。

(1)初始化OPTR栈,将表达式起始符“#”压入OPTR栈。

(2)扫描表达式,读入第一个字符ch,如果表达式没有扫描完毕至“=”或OPTR的栈顶元素不为“#”时,则循环执行以下操作。

  • ch不是运算符,则加入字符串Postfix
  • ch是运算符,则根据OPTR的栈顶元素和ch的优先级比较结果,进行以下不同的处理。

        a.若是小于,则ch压入OPTR栈,读入下一字符ch

        b.若是大于,则弹出OPTR栈顶的运算符,加入字符串Postfix

        c.若是等于,则OPTR的栈顶元素是“(”且ch是“)”,这时弹出OPTR栈顶的“(”,相当于括号匹配成功,然后读入下一字符ch

(3)字符串Postfix中的元素即为后缀表达式,返回后缀表达式。

3.2 设计代码

#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//------顺序栈的存储结构------
#define MAXSIZE 100  //顺序栈存储空间的初始分配量
typedef char SElemType;
typedef struct
{SElemType *base; //栈底指针SElemType *top;  //栈顶指针int stacksize;   //栈可用的最大容量
}SqStack;
//Status是函数返回值类型,其值是函数结果状态代码
typedef int Status;
Status InitStack(SqStack &S);
Status Push(SqStack &S, SElemType e);
Status Pop(SqStack &S, SElemType &e);
SElemType GetTop(SqStack S);
//表达式求值相关算法
bool In(SElemType ch);
SElemType Precede(SElemType optr, SElemType ch);
//中缀表达式转化为后缀表达式
string ztoh(char ch);
int main() {char ch;while (true) {cin >> ch;if ('=' == ch) {break;}string res = ztoh(ch);cout << res << endl;}return 0;
}
string ztoh(char ch)
{SqStack OPTR;InitStack(OPTR);Push(OPTR, '#');string Postfix = "";//表达式未读完 或 OPTR栈有运算符while (ch != '=' || GetTop(OPTR) != '#'){if (!In(ch)) {Postfix.push_back(ch);cin >> ch;}else {switch (Precede(GetTop(OPTR), ch)){case '<':Push(OPTR, ch); cin >> ch;break;case '>':char theta;Pop(OPTR, theta);Postfix.push_back(theta);break;case '=':char x;Pop(OPTR, x); cin >> ch;break;}}}return Postfix;
}
//判定读入的字符ch是否为运算符的函数
bool In(SElemType ch)
{if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||ch == '(' || ch == ')' || ch == '=') {return true;}return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
SElemType Precede(SElemType optr, SElemType ch)
{//规则(1)先乘除,后加减if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {return '<';}else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {return '>';}//规则(2)从左算到右if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {return '>';}//规则(3)先括号内,后括号外//optr不会出现右括号if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {if (ch == '(') {return '<';}else if (ch == ')' || ch == '=') {return '>';}}else if (optr == '(') {if (ch == ')') {return '=';}else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {return '<';}}//optr中只有'#'的情况if (optr == '#' &&(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {//ch肯定不是'='return '<';}
}
Status InitStack(SqStack &S)
{S.base = new SElemType[MAXSIZE];if (!S.base) exit(OVERFLOW);S.top = S.base;S.stacksize = MAXSIZE;return OK;
}
Status Push(SqStack &S, SElemType e)
{if (S.top - S.base == S.stacksize) return ERROR;*S.top++ = e;return OK;
}
Status Pop(SqStack &S, SElemType &e)
{if (S.top == S.base) return ERROR;e = *--S.top;return OK;
}
SElemType GetTop(SqStack S)
{if (S.top != S.base) {return *(S.top - 1);}
}

3.3 执行结果

图3 中缀表达式转化为后缀表达式代码执行结果

4 小题二:基于栈的后缀算术表达式求值

        借助一个运算数栈可对后缀表达式进行求值。

4.1 解题思路

        将中缀表达式转换为后缀表达式之后,对转换后得到的后缀表达式进行计算的具体步骤如下。

        借助一个工作栈OPND,用以寄存操作数或运算结果。从左到右扫描后缀表达式,读入第一个字符ch。若ch不是运算符,则压入OPND栈,读入下一字符;若ch是运算符,则从OPND栈中依次弹出两个数分别到YX,然后以“X ch Y”的形式计算出结果,将结果压入OPND栈中。如果后缀表达式未读完,重复执行上面过程,最后OPND栈顶元素即为表达式的求值结果,返回此元素。

4.2 设计代码

#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//Status是函数返回值类型,其值是函数结果状态代码
#define MAXSIZE 100 //顺序栈存储空间的初始分配量
typedef int Status;
typedef char SElemType;
typedef struct
{double *base; //栈底指针double *top;  //栈顶指针int stacksize;//栈可用的最大容量
}SqStack2;
Status InitStack2(SqStack2 &S);
Status Push2(SqStack2 &S, double e);
Status Pop2(SqStack2 &S, double &e);
double GetTop2(SqStack2 S);
//表达式求值相关函数
bool In(SElemType ch);
SElemType Precede(SElemType optr, SElemType ch);
double Operate(double x, SElemType theta, double y);
//计算后缀表达式
double calh(SElemType ch);
int main() {SElemType ch;while (true) {cin >> ch;if ('=' == ch) {break;}double res = calh(ch);//保留小数点后面2位cout << fixed << setprecision(2) << res << endl;}return 0;
}
double calh(SElemType ch)
{SqStack2 OPND;InitStack2(OPND);while (ch != '='){if (!In(ch)) {//读入的数没有多位数、小数Push2(OPND, double(ch - 48));}else {double x, y;Pop2(OPND, y);Pop2(OPND, x);Push2(OPND, Operate(x, ch, y));}cin >> ch;}return GetTop2(OPND);
}
//判定读入的字符ch是否为运算符的函数
bool In(SElemType ch)
{if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||ch == '(' || ch == ')' || ch == '=') {return true;}return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
SElemType Precede(SElemType optr, SElemType ch)
{//规则(1)先乘除,后加减if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {return '<';}else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {return '>';}//规则(2)从左算到右if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {return '>';}//规则(3)先括号内,后括号外//optr不会出现右括号if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {if (ch == '(') {return '<';}else if (ch == ')' || ch == '=') {return '>';}}else if (optr == '(') {if (ch == ')') {return '=';}else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {return '<';}}//optr中只有'#'的情况if (optr == '#' &&(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {//ch肯定不是'='return '<';}
}
//进行二元运算的函数
double Operate(double x, SElemType theta, double y)
{double z;switch (theta){case '+':z = x + y;break;case '-':z = x - y;break;case '*':z = x * y;break;case '/':z = x / y;break;default:z = 0;}return z;
}
Status InitStack2(SqStack2 &S)
{S.base = new double[MAXSIZE];if (!S.base) exit(OVERFLOW);S.top = S.base;S.stacksize = MAXSIZE;return OK;
}
Status Push2(SqStack2 &S, double e)
{if (S.top - S.base == S.stacksize) return ERROR;*S.top++ = e;return OK;
}
Status Pop2(SqStack2 &S, double &e)
{if (S.top == S.base) return ERROR;e = *--S.top;return OK;
}
double GetTop2(SqStack2 S)
{if (S.top != S.base) {return *(S.top - 1);}
}

4.3 执行结果

图4 基于栈的后缀算术表达式求值代码执行结果

5 基于栈的后缀算术表达式求值实验

        在基于栈的后缀算术表达式求值实验中,借助栈,可以将中缀算术表达式转换为后缀表达式,并基于后缀表达式求值。

5.1 解题思路

        将小题一和小题二合并起来,则可完成基于栈的后缀算术表达式实验。

5.2 设计代码

#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//Status是函数返回值类型,其值是函数结果状态代码
#define MAXSIZE 100  //顺序栈存储空间的初始分配量
typedef int Status;
typedef struct
{char *base; //栈底指针char *top;  //栈顶指针int stacksize;   //栈可用的最大容量
}SqStack;
Status InitStack(SqStack &S);
Status Push(SqStack &S, char e);
Status Pop(SqStack &S, char &e);
char GetTop(SqStack S);
typedef struct
{double *base; //栈底指针double *top;  //栈顶指针int stacksize;//栈可用的最大容量
}SqStack2;
Status InitStack2(SqStack2 &S);
Status Push2(SqStack2 &S, double e);
Status Pop2(SqStack2 &S, double &e);
double GetTop2(SqStack2 S);
//表达式求值相关函数
bool In(char ch);
char Precede(char optr, char ch);
double Operate(double x, char theta, double y);
//中缀表达式 转 后缀表达式
string ztoh(char ch);
//计算后缀表达式
int calh(string res);
int main() {char ch;while (true) {cin >> ch;if ('=' == ch) {break;}string res = ztoh(ch);cout << res << endl;int r = calh(res);cout << r << endl;}return 0;
}
string ztoh(char ch)
{SqStack OPTR;InitStack(OPTR);Push(OPTR, '#');string Postfix = "";while (ch != '=' || GetTop(OPTR) != '#'){if (!In(ch)) {Postfix.push_back(ch);cin >> ch;}else {switch (Precede(GetTop(OPTR), ch)){case '<':Push(OPTR, ch); cin >> ch;break;case '>':char theta;Pop(OPTR, theta);Postfix.push_back(theta);break;case '=':char x;Pop(OPTR, x); cin >> ch;break;}}}return Postfix;
}
int calh(string res)
{SqStack2 OPND;InitStack2(OPND);int i = 0;char ch = res[i++];while (ch != '\0'){if (!In(ch)) {Push2(OPND, double(ch - 48));}else {double x, y;Pop2(OPND, y);Pop2(OPND, x);Push2(OPND, Operate(x, ch, y));}ch = res[i++];//cin >> ch;}return GetTop2(OPND);
}
//判定读入的字符ch是否为运算符的函数
bool In(char ch)
{if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||ch == '(' || ch == ')' || ch == '=') {return true;}return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
char Precede(char optr, char ch)
{//规则(1)先乘除,后加减if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {return '<';}else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {return '>';}//规则(2)从左算到右if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {return '>';}//规则(3)先括号内,后括号外//optr不会出现右括号if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {if (ch == '(') {return '<';}else if (ch == ')' || ch == '=') {return '>';}}else if (optr == '(') {if (ch == ')') {return '=';}else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {return '<';}}//optr中只有'#'的情况if (optr == '#' &&(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {//ch肯定不是'='return '<';}
}
//进行二元运算的函数
double Operate(double x, char theta, double y)
{double z;switch (theta){case '+':z = x + y;break;case '-':z = x - y;break;case '*':z = x * y;break;case '/':z = x / y;break;default:z = 0;}return z;
}
Status InitStack(SqStack &S)
{S.base = new char[MAXSIZE];if (!S.base) exit(OVERFLOW);S.top = S.base;S.stacksize = MAXSIZE;return OK;
}
Status Push(SqStack &S, char e)
{if (S.top - S.base == S.stacksize) return ERROR;*S.top++ = e;return OK;
}
Status Pop(SqStack &S, char &e)
{if (S.top == S.base) return ERROR;e = *--S.top;return OK;
}
char GetTop(SqStack S)
{if (S.top != S.base) {return *(S.top - 1);}
}
Status InitStack2(SqStack2 &S)
{S.base = new double[MAXSIZE];if (!S.base) exit(OVERFLOW);S.top = S.base;S.stacksize = MAXSIZE;return OK;
}
Status Push2(SqStack2 &S, double e)
{if (S.top - S.base == S.stacksize) return ERROR;*S.top++ = e;return OK;
}
Status Pop2(SqStack2 &S, double &e)
{if (S.top == S.base) return ERROR;e = *--S.top;return OK;
}
double GetTop2(SqStack2 S)
{if (S.top != S.base) {return *(S.top - 1);}
}

5.3 执行结果

图5 基于栈的后缀算术表达式求值实验代码执行结果

6 解题心得

  • 借助一个运算符栈,可以将中缀表达式转换为后缀表达式。
  • 借助一个运算数栈,可以对后缀表达式进行求值。
  • 基于栈的中缀算术表达式求值算法,和基于栈的后缀算术表达式求值算法,是两种重要的表达式求值算法。

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

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

相关文章

Java基础数据结构之哈希表

概念 顺序结构以及平衡树 中&#xff0c;元素关键码与其存储位置之间没有对应的关系&#xff0c;因此在 查找一个元素时&#xff0c;必须要经过关键 码的多次比较 。 顺序查找时间复杂度为 O(N) &#xff0c;平衡树中为树的高度&#xff0c;即 O( log2N ) &#xff0c;搜索的效…

防火墙用户认证、NAT、策略路由、DNS透明代理以及双机热备笔记

用户认证 防火墙管理员登录认证 --- 检验身份的合法性&#xff0c;划分身份权限 用户认证 --- 上网行为管理的一部分 用户&#xff0c;行为&#xff0c;流量 --- 上网行为管理三要素 用户认证的分类 上网用户认证 --- 三层认证 --- 所有的跨网段的通信都可以属于上网行为。…

【Web前端实操19】商城官网_分析与顶部广告

本次实操主要是借用小米之前的网站来进行参考&#xff0c;达成网站静态页面开发的目的&#xff0c;而新学者想要一次性直接开发整个网站&#xff0c;肯定会很懵圈&#xff0c;因此&#xff0c;这个商城官网我会一部分一部分地进行拆分来写&#xff0c;最后合成整个界面。 本次…

国企重组整合后,如何严把“选人用人”关?

三年国企改革之后&#xff0c;新一轮国企改革明确重组方向&#xff0c;强调将“战略性重组和专业化整合”作为深化供给侧结构性改革从而建设世界一流企业的重要抓手&#xff0c;推动了国有资源的高效运转流动。在很多企业兼并、重组后&#xff0c;成立了新的集团性公司&#xf…

Mysql查询数据

1 基本查询语句 MySQL从数据表中查询数据的基本语句为SELECT语句。SELECT语句的基本格式是&#xff1a; 2 单表查询 2.1 查询所有字段 SELECT * FROM 表名; 2.2 在SELECT语句中指定所有字段 SELECT f_id, s_id ,f_name, f_price FROM fruits; 2.3 查询单个字段 SELECT 列名FR…

nginx部署前端(vue)项目及配置修改

目录 一、前端应用打包 二、部署前端应用 1、上传前端文件夹 2、修改nginx配置文件 3、重启nginx 三、查看效果 nginx安装参考&#xff1a;linux安装nginx-CSDN博客 一、前端应用打包 打包命令 npm run build 打包成功如下&#xff0c;会在项目路径下生成dist文件夹 二…

Windows Server 2003 Web服务器搭建

系列文章目录 目录 系列文章目录 前言 一、Web服务器是什么&#xff1f; 二、配置服务器 1.实验环境搭建 2.服务器搭建 1)控制面板中找到增加或删除程序打开 2)点击增加程序 3)安装Web服务器 4)查看安装是否成功 5)打开Internet信息服务(IIS)管理器,进行配置 6)找…

【开源之美】:cppcheck

一、项目链接 https://github.com/danmar/cppcheck/tree/main 二、效果示例

图像复原的天花板在哪里?SUPIR:开创性结合文本引导先验和模型规模扩大

SUPIR&#xff08;Scaling-UP Image Restoration&#xff09;&#xff0c;这是一种开创性的图像复原方法&#xff0c;利用生成先验和模型扩大规模的力量。通过利用多模态技术和先进的生成先验&#xff0c;SUPIR在智能和逼真的图像复原方面取得了重大进展。作为SUPIR中的关键催化…

如何配置MacLinuxWindows环境变量

这里写目录标题 什么是环境变量什么是PATH为什么要配置环境变量 如何配置环境变量环境变量有哪些环境变量加载顺序环境变量加载详解 配置参考方法一&#xff1a; export PATHLinux环境变量配置方法二&#xff1a;vim ~/.bashrcLinux环境变量配置方法三&#xff1a;vim ~/.bash_…

go语言基础之泛型

1.泛型 泛型是一种独立于所使用的特定类型的编写代码的方法。使用泛型可以编写出适用于一组类型中的任何一种的函数和类型。 1.1 为什么需要泛型 func reverse(s []int) []int {l : len(s)r : make([]int, l)for i, e : range s {r[l-i-1] e}return r }fmt.Println(reverse…

红外热成像仪定制_热成像仪/红外夜视仪开发方案

红外热成像技术是一种利用红外热成像仪将物体发出的不可见红外辐射能量转换成可见的温度场图像的技术&#xff0c;通过不同颜色来表示不同温度。这项技术的应用领域非常广泛&#xff0c;从电路维修到暖通检测再到汽车故障排查等各个领域都有着重要的作用。 红外热成像仪的解决方…

SpringBoot项目实现热部署的配置方法

SpringBoot项目实现热部署的配置方法 1、什么是热部署&#xff1f; 热部署&#xff0c;就是在应用正在运行的时候升级软件&#xff0c;却不需要重新启动应用。 2、什么是SpringBoot热部署&#xff1f; SpringBoot热部署就是在项目正在运行的时候修改代码, 却不需要重新启动…

PCB的通孔,盲孔,埋孔

通孔&#xff1a;双层板从顶层到底层的打通&#xff0c;这样电流就能够从顶层到底层 盲孔&#xff1a;因为看不到底&#xff0c;像一口井一样&#xff0c;只能打到中间&#xff0c;里面灌上铜&#xff0c;我们可以从第一层切换到第二层&#xff0c;第三层等等&#xff0c;盲孔…

在Windows上安装与配置Apache服务并结合内网穿透工具实现公网远程访问本地内网服务

文章目录 前言1.Apache服务安装配置1.1 进入官网下载安装包1.2 Apache服务配置 2.安装cpolar内网穿透2.1 注册cpolar账号2.2 下载cpolar客户端 3. 获取远程桌面公网地址3.1 登录cpolar web ui管理界面3.2 创建公网地址 4. 固定公网地址 前言 Apache作为全球使用较高的Web服务器…

[React源码解析] Fiber

在React15及以前, Reconciler采用递归的方式创建虚拟Dom, 但是递归过程不可以中断, 如果组件的层级比较深的话, 递归会占用线程很多时间, 那么会造成卡顿。 为了解决这个问题, React16将递归的无法中断的更新重构为异步的可中断更新, Fiber架构诞生。 文章目录 1.Fiber的结构2…

【blender烘焙】法线烘焙出现大面积结构丢失怎么办?blender烘焙vs八猴烘焙

用dcc烘焙法线是很常用的减面优化手段&#xff0c;很多建模的dcc自己也内置的烘焙的功能&#xff0c;像我自己在工作流中也偶尔用blender的烘焙做一下材质的整合优化&#xff0c;在质量要求不高的时候还算凑合可用。 问题描述 在前期的文章中飞燕2号建模&#xff0c;我就遇到…

数据防泄密方案公司(dlp数据防泄密厂商排名)

在当今数字化时代&#xff0c;数据已经成为了企业最重要的资产之一。然而&#xff0c;随着企业信息化的不断深入&#xff0c;数据泄露的风险也越来越大。为了保护企业的核心数据&#xff0c;越来越多的企业开始重视数据防泄密工作&#xff0c;并寻求专业的数据防泄密方案提供商…

LeetCode——415. 字符串相加

C开头 &#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️Take your time ! &#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#…

【计网·湖科大·思科】实验五 IPV4地址-分类地址和构建超网

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…