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

1 题目描述

图1 题目描述

2 题目解读

这一题需要使用栈,完成中缀算术表达式的求值。另外,这一题要求算法具有拼数功能,拼数功能的实现,可以使用C语言库函数atof()函数来完成。

3 解法一:基于顺序栈的中缀算术表达式求值

顺序栈是使用顺序存储结构实现的栈,在栈数据结构中很重要。

3.1 解题思路

1、初始化OPTR栈和OPND栈,将表达式起始符"#"压入OPTR栈。

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

(1)若ch不是运算符,则将从ch到下一个运算符之前的字符拼成双精度浮点数,压入OPND栈,读入下一字符ch。

(2)若ch是运算符,则根据OPTR的栈顶元素和ch的优先级比较结果,做不同的处理。

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

b.若大于,则弹出OPTR栈顶的运算符,从OPND栈弹出两个数,进行相应运算,将结果压入OPND栈;

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

3、OPND栈顶元素即表达式求值结果,返回此元素。

3.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);
//表达式求值算法
double EvaluateExpression(char ch);
//算法调用的3个函数
bool In(char ch);
char Precede(char optr, char ch);
double Operate(double x, char theta, double y);
int main() {
	char ch;
	while (true) {
		cin >> ch;
		if ('=' == ch) {
			break;
		}
		double res = EvaluateExpression(ch);
		//保留小数点后面2位
		cout << fixed << setprecision(2) << res << endl;
	}
	return 0;
}
double EvaluateExpression(char ch)
{
	SqStack2 OPND;   //操作数栈
	SqStack OPTR;    //运算符栈
	InitStack2(OPND);//初始化OPND栈
	InitStack(OPTR); //初始化OPTR栈
	Push(OPTR, '#');
	double x;
	while (ch != '=' || GetTop(OPTR) != '#')
	{
		if (!In(ch)) {//如果ch不是运算符
			char str[100] = { 0 };
			int i = 0;
			while (!In(ch)) {//拼数时ch可能是'='
				str[i++] = ch;
				cin >> ch;
			}
			x = atof(str);
			Push2(OPND, x);
		}
		else {
			//比较OPTR的栈顶元素和ch的优先级
			switch (Precede(GetTop(OPTR), ch))
			{
			case '<':
				Push(OPTR, ch); cin >> ch;
				break;
			case '>':
				char theta;
				double a, b;
				Pop(OPTR, theta);
				Pop2(OPND, b);
				Pop2(OPND, a);
				Push2(OPND, Operate(a, theta, b));
				break;
			case '=':
				char x;
				Pop(OPTR, x); cin >> ch;
				break;
			}
		}
	}
	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);
	}
}

3.3 执行结果

图2 基于顺序栈的中缀算术表达式求值代码执行结果

4 解法二:基于链栈的中缀算术表达式求值

链栈是使用链式存储结构实现的栈,是栈的另一种实现方式。

4.1 解题思路

基于链栈的解题思路与基于顺序栈的解题思路一样,不同的只是栈的实现方式。

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 struct StackNode
{
	char data;
	struct StackNode *next;
}StackNode, *LinkStack;
Status InitStack(LinkStack &S);
Status Push(LinkStack &S, char e);
Status Pop(LinkStack &S, char &e);
char GetTop(LinkStack S);
typedef struct StackNode2
{
	double data;
	struct StackNode2 *next;
}StackNode2, *LinkStack2;
Status InitStack2(LinkStack2 &S);
Status Push2(LinkStack2 &S, double e);
Status Pop2(LinkStack2 &S, double &e);
double GetTop2(LinkStack2 S);
//表达式求值算法
double EvaluateExpression(char ch);
//算法调用的3个函数
bool In(char ch);
char Precede(char optr, char ch);
double Operate(double x, char theta, double y);
int main() {
	char ch;
	while (true) {
		cin >> ch;
		if ('=' == ch) {
			break;
		}
		double res = EvaluateExpression(ch);
		//保留小数点后面2位
		cout << fixed << setprecision(2) << res << endl;
	}
	return 0;
}
double EvaluateExpression(char ch)
{
	LinkStack2 OPND; //操作数栈
	LinkStack OPTR;  //运算符栈
	InitStack2(OPND);//初始化OPND栈
	InitStack(OPTR); //初始化OPTR栈
	Push(OPTR, '#');
	double x;
	while (ch != '=' || GetTop(OPTR) != '#')
	{
		if (!In(ch)) {//如果ch不是运算符
			char str[100] = { 0 };
			int i = 0;
			while (!In(ch)) {//拼数时ch可能是'='
				str[i++] = ch;
				cin >> ch;
			}
			x = atof(str);
			Push2(OPND, x);
		}
		else {
			//比较OPTR的栈顶元素和ch的优先级
			switch (Precede(GetTop(OPTR), ch))
			{
			case '<':
				Push(OPTR, ch); cin >> ch;
				break;
			case '>':
				char theta;
				double a, b;
				Pop(OPTR, theta);
				Pop2(OPND, b);
				Pop2(OPND, a);
				Push2(OPND, Operate(a, theta, b));
				break;
			case '=':
				char x;
				Pop(OPTR, x); cin >> ch;
				break;
			}
		}
	}
	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(LinkStack &S)
{
	//构造一个空栈S,栈顶指针置空
	S = NULL;
	return OK;
}
Status Push(LinkStack &S, char e)
{
	//在栈顶插入元素e
	StackNode *p = new StackNode;
	p->data = e;
	p->next = S;
	S = p;
	return OK;
}
Status Pop(LinkStack &S, char &e)
{
	//删除S的栈顶元素,用e返回其值
	if (S == NULL) return ERROR;
	e = S->data;
	StackNode *p = S;
	S = S->next;
	delete p;
	return OK;
}
char GetTop(LinkStack S)
{
	//返回S的栈顶元素,不修改栈顶指针
	if (S != NULL) {     //栈非空
		return S->data;  //返回栈顶元素的值,栈顶指针不变
	}
}
Status InitStack2(LinkStack2 &S)
{
	//构造一个空栈S,栈顶指针置空
	S = NULL;
	return OK;
}
Status Push2(LinkStack2 &S, double e)
{
	//在栈顶插入元素e
	StackNode2 *p = new StackNode2;
	p->data = e;
	p->next = S;
	S = p;
	return OK;
}
Status Pop2(LinkStack2 &S, double &e)
{
	//删除S的栈顶元素,用e返回其值
	if (S == NULL) return ERROR;
	e = S->data;
	StackNode2 *p = S;
	S = S->next;
	delete p;
	return OK;
}
double GetTop2(LinkStack2 S)
{
	//返回S的栈顶元素,不修改栈顶指针
	if (S != NULL) {   //栈非空
		return S->data;//返回栈顶元素的值,栈顶指针不变
	}
}

4.3 执行结果

图3 基于链栈的中缀算术表达式求值代码执行结果

5 解题心得

  • 栈这一数据结构,如果使用自己实现的,则代码较多,工作量较大。
  • 表达式求值算法,可以完成中缀算术表达式的求值。
  • 代码的完善是一项费时的工作。
相关推荐
TN_stark9329 分钟前
多进程/线程并发服务器
服务器·算法·php
汉克老师34 分钟前
GESP4级考试语法知识(贪心算法(四))
开发语言·c++·算法·贪心算法·图论·1024程序员节
smj2302_796826521 小时前
用枚举算法解决LeetCode第3348题最小可整除数位乘积II
python·算法·leetcode
CyberMuse1 小时前
表的数据结构和常见操作
数据结构
爱吃生蚝的于勒1 小时前
C语言最简单的扫雷实现(解析加原码)
c语言·开发语言·学习·计算机网络·算法·游戏程序·关卡设计
姆路1 小时前
QT中使用图表之QChart绘制动态折线图
c++·qt
小五Z2 小时前
MySql--增删改查表设计总结
数据结构·数据库·mysql
秋说2 小时前
【数据结构 | C++】整型关键字的平方探测法散列
数据结构·c++·算法
weixin_478689762 小时前
【回溯法】——组合总数
数据结构·python·算法
戊子仲秋3 小时前
【LeetCode】每日一题 2024_11_14 统计好节点的数目(图/树的 DFS)
算法·leetcode·深度优先