C++ 数据结构 算数表达式求值——栈存储

数据结构实习——算术表达式求值

一、实习题目及要求

1、题目:算术表达式求值
2、要求:
(1)正确解释表达式;
(2)符合四则运算规则:
(3)先乘除、后加减;
(4)从左到右运算;
(5)先括号内,后括号外;
(6)输出最后的计算结果。

二、问题描述

1、对一个合法的中缀表达式求值。
2、假设表达式只包含+、-、*、/ 四个双目运算符,并且允许有括号出现,运算符本身不具有二义性。

三、问题分析

1、本题要求我们计算算数表达式的值,在程序中需要大量的重复运用栈的基本操作。而在程序设计过程需要考虑到运算符的优先级,并需要熟悉栈的操作从而进行计算 。
2、表达式可看做是一个字符串,其中字符分为操作数和运算符两类,因此需要两个工作栈,命名为OPTR(运算符栈)和OPND(操作数栈)。以此判断字符串中的字符,如果是操作数则压入操作数栈,如果是运算符栈,则压入运算符栈,然后再进行弹栈操作。
3、本题还需考虑到小数问题,即把小数点之后的数字和之前的数字分开处理,然后再一同压入栈中。
4、此外还需考虑到负数问题,在输入命令时,负数需要添加括号,通过扫描,当字符串中的字符前一位时‘)’后一位时‘-’时,则需要在操作数栈中先压入一个数0,然后再进行后续的运算,记得实现负数的运算。

四、设计思想

1、本题我首先设计了一个判断运算优先级的函数char Precede(char a, char b)该函数参考老师给的ppt里的代码,并进行了细微的改动。
2、然后定义了函数bool Operate(double a, char theta, double b, double &r),用于计算二元表达式的值,这里需要注意,当除数为零的时候应该返回错误信息,并提醒用户,所以这里我把该函数定义成bool型。
3、在压栈的时候需虑到,字符串中的字符是那种类型,如果是运算符,则需要压进运算栈,如果是操作数,则压进操作数栈,此时,则需要编写一个函数进行字符类型的判断,这里,我编写了一个函数bool IsOperator(char ch),用于判断字符ch是否为运算符,如果是运算符在后面压栈的时候则压进运算符栈,如果不是,而是操作数则压入操作数栈。
4、除了需要判断字符是何种类型之外,还需要判断的是,我们在输入表达式时,输入的括号是否配对,如果配对的话,则可以继续计算,如果不能配对的话,则需提醒用户,为了实现该功能,我便编写了函数bool Check(char s[])用于检查括号是否配对。
接下来则是最重要的,设计具体的计算函数,这里我编写了一个bool Calculate(char s[], double &result) 函数,用于计算表达式结果,在函数的定义过程中,我把函数的类型定义成了bool型,是因为我该函数的编写过程中,我调用了前面编写的bool型的计算二元表达式值得函数,从而在后续函数调用(计算)时能够实现计算并判断函数是否符合规则,即判断当计算除法时,除数是否为0;首先,当遇到小数点得时候,则需要把小数点之后的数字和之前的数字分开处理,用point标记出现小数点后第x位,在继续查找记录数字, 至运算符结束。整数部分为num = num * 10 + (s[j] - 48),即非小数时num = num * 10 + (s[j] - 48);这里由于输入得数属于字符型,根据ascll码把字符串的数减去48即使我们需要计算的数。
然后判断输入的字符是否时运算符,然后根据运算符的优先级进行压栈,然后把操纵数压入操作数栈,再进行弹栈计算,这里,进行是否是负数的判断,如果字符是‘)’且它的后一位是‘-’,则需要往操作数栈里先压入一个0,然后再进行后续计算,则可以实现负数的运算。
5、在主函数中,即要求输入表达式之后,需要转换成规格化表达式,补“/0”和“=”。

五、设计流程图

在这里插入图片描述

六、附录

【源代码】

#include "stdafx.h"
#include <cmath>
#include <stack>
#include <string>
#include<iostream>
using namespace std;

//判断运算符优先级
char Precede(char a, char b) { //判断运算符优先级
	int i, j;
	char Table[8][8] = {
		{ ' ', '+', '-', '*', '/', '(', ')', '=' },
		{ '+', '>', '>', '<', '<', '<', '>', '>' },
		{ '-', '>', '>', '<', '<', '<', '>', '>' },
		{ '*', '>', '>', '>', '>', '<', '>', '>' },
		{ '/', '>', '>', '>', '>', '<', '>', '>' },
		{ '(', '<', '<', '<', '<', '<', '=', ' ' },
		{ ')', '>', '>', '>', '>', ' ', '>', '>' },
		{ '=', '<', '<', '<', '<', '<', ' ', '=' }
	};  //优先级表格

	for (i = 0; i < 8; i++)
		if (Table[0][i] == a)  //寻找运算符a
			break;

	for (j = 0; j < 8; j++) //寻找运算符b
		if (Table[j][0] == b)
			break;

	return Table[j][i];
}

//计算二元表达式的值
bool Operate(double a, char theta, double b, double &r) { //计算二元表达式的值
	if (theta == '+')
		r = a + b;

	else if (theta == '-')
		r = a - b;

	else if (theta == '*')
		r = a * b;

	else {
		if (fabs(b - 0.0) < 1e-8)  //如果除数为0,返回错误信息
			return false;
		else
			r = a / b;
	}

	return true;
}

//判断字符ch是否为运算符
bool IsOperator(char ch) { //判断字符ch是否为运算符
	char ptr[10] = { '+', '-', '*', '/', '(', ')', '=' };
	int i;

	for (i = 0; i < 7; i++) {
		if (ch == ptr[i])
			return true;
	}

	return false;
}

//检查表达式括号是否匹配
bool Check(char s[]) { //检查表达式括号是否匹配
	int flag = 0, i;

	for (i = 0; s[i] != 0; i++) {
		if (s[i] == '(')
			flag++;

		if (s[i] == ')')
			flag--;
	}

	if (flag)
		return false;
	else
		return true;
}

//计算表达式的结果
bool Calculate(char s[], double &result) { //计算表达式的结果
	char theta;
	int i = 0, j, point = 0;
	double a, b, r, num = 0;

	stack<double> OPND;  //数字栈
	stack<char> OPTR;  //运算符栈

	OPTR.push('=');

	while (s[i] != '=' || OPTR.top() != '=') { //对表达式a进行计算
		if ((s[i] >= '0' && s[i] <= '9') || s[i] == '.') { //字符是数字或者小数点
			num = 0;  //初始化数字为0
			point = 0;  //point用来标记是否出现小数点以及当前处于小数点后第x位,point==10^x

			if (s[i] == '.')
				point = 10;
			else
				num = s[i] - 48;
			j = i + 1;

			while (!IsOperator(s[j])) { //继续往后查找并记录该数字,直到该数字结束遇到运算符为止
				if (s[j] == '.') {
					point = 10;
					j++;
					continue;
				}

				if (!point)  //整数部分
					num = num * 10 + (s[j] - 48);
				else {
					num = num + 1.0 * (s[j] - 48) / point;  //小数部分
					point *= 10;  //小数位数后移一位
				}

				j++;
			}

			i = j;
			OPND.push(num);  //将该数字压入栈中
		}

		else if (IsOperator(s[i])) { //字符是运算符
			if (s[0] == '-'){//负数
				num = 0;
				OPND.push(num);
			}
			else if (s[i] == '('&&s[i + 1] == '-'){//负数
				num = 0;
				OPND.push(num);
			}

			switch (Precede(s[i], OPTR.top())) { //该运算符和栈顶运算符进行优先级比较并做相关处理
			case '<':
				OPTR.push(s[i++]);
				break;
			case '=':
				OPTR.pop();
				i++;
				break;
			case '>':
				theta = OPTR.top();  //从栈中弹出一个运算符进行计算
				OPTR.pop();
				b = OPND.top();  //弹出两个数字,注意顺序,先弹出的数是第二个操作数
				OPND.pop();
				a = OPND.top();
				OPND.pop();
				if (Operate(a, theta, b, r))  //计算并判断是否有除数等于0的情况
					OPND.push(r);  //若正常,则将结果压入栈中
				else
					return false;  //出现除数为0的情况,返回错误信息
				break;
			}
		}
	}
	result = OPND.top();  //最后数字栈中的数即为表达式的最终结果
	return true;
}

int main()
{
	int i, j;
	char s1[100], s2[100];

	double result;

	cout << "Please input an expression to enter with the Enter key:" << endl;
	cout << "(If you want to calculate negative numbers, add brackets to negative numbers.)" << endl;

	while (cin >> s1) {                          //输入表达式
		if (strlen(s1) == 1 && s1[0] == '0')
			break;

		cout << "The value of this expression is:";
		cout << endl;

		for (i = 0, j = 0; s1[i] != 0; i++) {   //将表达式转换为规格化的表达式,并在末尾加上“=”,保存在s2中
			if (s1[i] == ' ')
				continue;
			s2[j++] = s1[i];
		}
		s2[j++] = '=';
		s2[j] = '\0';
		puts(s2);

		if (Check(s2)) {       //检查括号是否匹配
			if (Calculate(s2, result))  //计算并检查表达式中是否出现除数为0的情况
				cout << result;
			else
				cout << "The divisor can not be 0. " << endl;
		}
		else
			cout << "Brackets do not match." << endl;
	}
	return 0;
}

【测试数据】

1、 3.5×(7+2) /(-6)(包括了+、—、*、/、(、)、小数、负数的运算)
2、 3/0(除数为0时)
3、 1+2×(3+4 (括号不匹配时)

【运行结果】

(1)
在这里插入图片描述
(2)
在这里插入图片描述
(3)
在这里插入图片描述

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值