数据结构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 解题心得

  • 栈这一数据结构,如果使用自己实现的,则代码较多,工作量较大。
  • 表达式求值算法,可以完成中缀算术表达式的求值。
  • 代码的完善是一项费时的工作。
相关推荐
火云洞红孩儿23 分钟前
基于AI IDE 打造快速化的游戏LUA脚本的生成系统
c++·人工智能·inscode·游戏引擎·lua·游戏开发·脚本系统
一只码代码的章鱼30 分钟前
排序算法 (插入,选择,冒泡,希尔,快速,归并,堆排序)
数据结构·算法·排序算法
青い月の魔女1 小时前
数据结构初阶---二叉树
c语言·数据结构·笔记·学习·算法
我要出家当道士1 小时前
Nginx单向链表 ngx_list_t
数据结构·nginx·链表·c
林的快手2 小时前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
FeboReigns2 小时前
C++简明教程(4)(Hello World)
c语言·c++
FeboReigns2 小时前
C++简明教程(10)(初识类)
c语言·开发语言·c++
千天夜2 小时前
多源多点路径规划:基于启发式动态生成树算法的实现
算法·机器学习·动态规划
zh路西法2 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
从以前2 小时前
准备考试:解决大学入学考试问题
数据结构·python·算法