数据结构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 设计代码

cpp 复制代码
#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 设计代码

cpp 复制代码
#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 设计代码

cpp 复制代码
#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 解题心得

  • 借助一个运算符栈,可以将中缀表达式转换为后缀表达式。
  • 借助一个运算数栈,可以对后缀表达式进行求值。
  • 基于栈的中缀算术表达式求值算法,和基于栈的后缀算术表达式求值算法,是两种重要的表达式求值算法。
相关推荐
蜀黍@猿17 分钟前
C/C++基础错题归纳
c++
古希腊掌管学习的神20 分钟前
[搜广推]王树森推荐系统笔记——曝光过滤 & Bloom Filter
算法·推荐算法
qystca21 分钟前
洛谷 P1706 全排列问题 C语言
算法
浊酒南街26 分钟前
决策树(理论知识1)
算法·决策树·机器学习
雨中rain32 分钟前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
就爱学编程34 分钟前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
学术头条39 分钟前
清华、智谱团队:探索 RLHF 的 scaling laws
人工智能·深度学习·算法·机器学习·语言模型·计算语言学
Schwertlilien1 小时前
图像处理-Ch4-频率域处理
算法
IT猿手1 小时前
最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解TP1-TP10及工程应用---盘式制动器设计,提供完整MATLAB代码
开发语言·深度学习·算法·机器学习·matlab·多目标算法
__lost1 小时前
MATLAB直接推导函数的导函数和积分形式(具体方法和用例)
数学·算法·matlab·微积分·高等数学