数据结构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.hqwc.cn/news/439899.html

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

相关文章

【BUG】golang gorm导入数据库报错 “unexpected type clause.Expr“

帮同事排查一个gorm导入数据报错的问题 事发现场 ck sql CREATE TABLE ods_api.t_sms_jg_msg_callback_dis (app_key String DEFAULT COMMENT 应用标识,callback_type Int32 DEFAULT 0 COMMENT 0送达&#xff0c;1回执,channel Int32 DEFAULT 0 COMMENT uid下发的渠道,mode…

文献速递:人工智能医学影像分割--- 深度学习分割骨盆骨骼:大规模CT数据集和基线模型

文献速递&#xff1a;人工智能医学影像分割— 深度学习分割骨盆骨骼&#xff1a;大规模CT数据集和基线模型 我们为大家带来人工智能技术在医学影像分割上的应用文献。 人工智能在医学影像分析中发挥着至关重要的作用&#xff0c;尤其体现在图像分割技术上。这项技术的目的是准…

Blender教程(基础)-面的细分与删除、挤出选区-07

一、Blender之面的细分 新建一个立方体&#xff0c;在编辑模式下、选中一个面。 在选中的面上单击右键弹出细分选项&#xff0c;选择细分。 在选中细分后、会默认细分1次。修改细分次数在左下角 二、Blender之面的删除 选择中需要操作的面&#xff0c;在英文状态下按X键弹…

51单片机通过级联74HC595实现倒计时秒表Protues仿真设计

一、设计背景 近年来随着科技的飞速发展&#xff0c;单片机的应用正在不断的走向深入。本文阐述了51单片机通过级联74HC595实现倒计时秒表设计&#xff0c;倒计时精度达0.05s&#xff0c;解决了传统的由于倒计时精度不够造成的误差和不公平性&#xff0c;是各种体育竞赛的必备设…

minio2023版本安装对象存储文件迁移

一、环境 minio版本&#xff1a;minio-20230320201618.0.0.x86_64.rpm 二、安装 将下载好的rpm包放在文件夹下&#xff0c;然后cd到该目录 sudo rpm -ivh minio-20230320201618.0.0.x86_64.rpm 三、启动 1、minio的位置 which minio cd /usr/local/bin 2、启动 &#xff08;可…

负载均衡下的webshell连接

一、环境配置 1.在Ubuntu上配置docker环境 我们选择用Xshell来将环境资源上传到Ubuntu虚拟机上&#xff08;比较简单&#xff09; 我们选择在root模式下进行环境配置&#xff0c;先将资源文件复制到root下&#xff08;如果你一开始就传输到root下就不用理会这个&#xff09; …

MySQL安装部署-集群版

MySQL支持主从复制策略&#xff0c;本文主要描述读写分离集群的安装部署。 如上所示&#xff0c;MySQL数据库的主从复制策略中&#xff0c;主数据库Master同步binary log中的数据到从数据库Slave A以及从数据库Slave B&#xff0c;主数据库Master可提供写服务以及部分读服务、从…

Netty核心——Reactor下篇(十)

任务队列中的Task有3种典型使用场景 用户程序自定义的普通任务 比如有一个非常耗时长的业务 异步执行提交该Channel对应的NioEventLoop的TaskQueue中 用户自定义定时任务 该任务提交到scheduleTaskQueue中 非当前Reactor线程调用Channel的各种方法 例如在推送系统的业务线程…

iOS 17.4 苹果公司正在加倍投入人工智能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

MySQL进阶45讲【8】MySQL事务到底是隔离的还是不隔离的?

1 前言 在 MySQL进阶45讲【3】事务隔离的恩恩怨怨 这篇文章和大家讲事务隔离级别的时候提到过&#xff0c;如果是可重复读隔离级别&#xff0c;事务T启动的时候会创建一个视图read-view&#xff0c;之后事务T执行期间&#xff0c;即使有其他事务修改了数据&#xff0c;事务T看…

【论文复现】基于CGAN的手写数字生成实验——模型改进

文章目录 2.4.1 超参数调整一、batch size二、 epochs三、 Adam&#xff1a;learning rate四、 Adam&#xff1a;weight_decay五、 n_critic 2.4.2 模型改进一、 超参数优化&#xff08;一&#xff09;batch size&#xff08;二&#xff09;learning rate&#xff08;三&#x…

嵌入式-PCB-两层板-开关控制LED

参考视频&#xff1a;《黑马程序员嵌入式开发入门模电&#xff08;模拟电路&#xff09;基础P50-P63集》 推荐&#xff1a; 《嘉立创PCB下单平台》 《技术指导&#xff1a;下单前技术员必看 》 《下单员必看事项 》 《工艺参数》 文章目录 需求初步画图软件选型关键字&#xf…