简易计算器(详解用栈实现算术表达式求值)

这篇具有很好参考价值的文章主要介绍了简易计算器(详解用栈实现算术表达式求值)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

[问题描述]

一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正实数,运算符只含加减乘除等四种运算符,界限符只含左右括号如:6+15*(21-8/4)。编程利用“运算符优先法”求算术表达式的值。

[基本要求]

(1)读入一个合法的算术表达式,输出正确的结果。

(2)考虑算法的健壮性,当表达式错误时,要给出错误原因的提示

(4)实现非整数的处理

实现一个简易计算器,我们要做的事情主要有两个。第一个是判断输入的表达式是否正确;第二个是若表达式正确,则求出运算结果。

1、判断算术表达式是否正确

判断 算术表达式是否正确要充分考虑各种可能的错误情况,这里 我列举出了几种错误情况:
(1) 括号不匹配(遗漏了左括号或右括号);
(2)出现非法字符,如 !/?& 等;
(3)运算符重复,例如1+3**2 中乘号重复;
(4)缺少用于运算的数,例如 (/3+2)+1 、*3+4、2+4/ 。
除了第一种情况外,其他情况都可以用 if 语句很容易地判断出来;但要判断括号是否匹配 就要运用到。这里算法思想是:依次读取表达式中的字符,若是非括号字符,则忽略。若是左括号,则压栈。若是右括号,则与栈顶元素进行匹配,若栈为空,则说明右括号多余(即遗漏了左括号),报错返回;若匹配成功,则栈顶元素弹栈(即一个左括号和一个右括号完成了配对),继续读取下一个字符。直到表达式遍历完毕,若此时栈不为空,则说明有左括号还没有匹配(即遗漏了右括号),报错返回;若栈为空,则说明括号全部配对成功。

2、求出运算结果

我们平常写出的算式对于我们来说是比较直观和容易计算的,这种形式的表达式被称为中缀表达式,但对于计算机而言,中缀表达式是无法直接进行计算的。因为在计算过程中,我们不是读到一个运算符就立即计算,而是要与后面的运算符进行优先级比较,以决定先算哪个,而计算机却做不到这点,但我们可以栈来解决这个问题。具体实现方法如下:

首先,我们创建两个栈 Num 和 Sign ,Num 保存运算数,Sign 保存运算符号。然后,我们依次读取表达式中的字符,若为运算数,则转化为对应数值压入 Num栈中;若为运算符,则和 Sign栈的栈顶运算符进行优先级比较:若栈顶运算符优先级低于当前运算符,则将当前运算符压入Sign栈中;若栈顶运算符优先级高于当前运算符,则 Sign栈弹出运算符、Num栈弹出两个运算数,进行相应计算,并将计算结果压入 Num栈中;但若为 ‘)’ ,则需要将对应左括号从 Sign栈中弹出,这就需要完成两个括号内的所有运算,计算方式同上。直到表达式遍历完毕,Num栈的栈顶元素即为最终结果。

算法思想如此,实现起来却并不简单。例如,我们要将运算数转化为对应数值,由于包括浮点数的情况,这个实现起来就比较复杂。以下是我的程序代码:

# include <iostream>
# include <algorithm>
# include <string>
# include <stack>
using namespace std;
bool CheckOperator(char c);             //判断是否为运算符
bool CheckMatch(string str);            //判断算式是否正确
int Prior(char c);                      //求符号的优先级
bool CheckPrior(char c1, char c2);      //通过优先级判断是否入栈
float CalNum(float a, float b, char c); //求单项运算的结果
float CalResult(string str);            //计算最终结果
int main()
{
	string str;
	cout << "请输入计算式:";
	cin >> str;
	str.append("#");           //作为算式的终止符
	bool t = CheckMatch(str);  //判断算式是否正确
	if (t) {
		float r = CalResult(str);  //计算最终结果
		cout << "计算结果为:" << r << endl;
	}
	return 0;
}

bool CheckOperator(char c)   //判断是否为运算符
{
	if (c == '+' || c == '-' || c == '*' || c == '/') {
		return true;
	}
	else {
		return false;
	}
}

bool CheckMatch(string str)  //判断算式是否正确
{
	if (CheckOperator(str[0]) || CheckOperator(str[str.length() - 2])) {
		//算式首尾位置出现运算符
		cout << "算术表达式错误!\n" << "原因:缺少用于运算的数" << endl;
		return false;
	}
	//判断括号及运算符输入是否正确
	stack<char>S;   //用于存放括号的栈
	for (int i = 0; i < str.length() - 1; i++) {
		if (!CheckOperator(str[i]) && (str[i] < 48 || str[i]>57) && str[i] != '(' && str[i] != ')' && str[i] != '.') {
			//出现非法字符
			cout << "算术表达式错误!\n" << "原因:出现非法字符" << str[i] << endl;
			return false;
		}
		if (CheckOperator(str[i]) && CheckOperator(str[i + 1])) {
			//运算符重复
			cout << "算术表达式错误!\n" << "原因:运算符" << str[i] << "重复" << endl;
			return false;
		}
		if (str[i] == '(')
		{
			S.push(str[i]);
			if (CheckOperator(str[i + 1])) {
				//'('后缺少用于运算的数
				cout << "算术表达式错误!\n" << "原因:缺少用于运算的数" << endl;
				return false;
			}
		}
		if (str[i] == ')')
		{
			if (S.empty()) {
				//若栈已为空,说明遗漏左括号
				cout << "算术表达式错误!\n" << "原因:遗漏左括号" << endl;
				return false;
			}
			S.pop();
		}
	}
	if (!S.empty()) {
		//若此时栈还不为空,说明遗漏右括号
		cout << "算术表达式错误!\n" << "原因:遗漏右括号" << endl;
		return false;
	}
	return true;
}

int Prior(char c)    //返回符号的优先级
{
	switch (c)
	{
	case '#': return 0;
	case '+':
	case '-': return 1;
	case '*':
	case '/': return 2;
	case ')': return 3;
	case '(': return 4;
	}
}

bool CheckPrior(char c1, char c2) //通过优先级判断是否入栈(c1当前,c2栈中)
{
	if (c2 == '(') {
		return true;  //同意进栈
	}
	int p1 = Prior(c1);
	int p2 = Prior(c2);
	if (p1 > p2) {   //当前运算符的优先级较高
		return true; //同意进栈
	}
	else {
		return false; //不同意进栈
	}
}

float CalNum(float a, float b, char c) //求单项运算的结果
{
	switch (c)
	{
	case '+': return a + b;
	case '-': return a - b;
	case '*': return a * b;
	case '/': return a / b;
	}
}

float CalResult(string str) //计算最终结果
{
	stack<float>Num;  //保存运算数的栈
	stack<char>Sign;  //保存运算符的栈
	Sign.push('#');
	float a, b;
	for (int i = 0; i < str.length() && !Sign.empty(); i++) {
		if (str[i] >= 48 && str[i] <= 57) {
			//若为数字
			a = float(str[i]) - 48;           //a保存由字符转化为数字后的值
			int j;
			//将字符转化为数字
			for (j = i + 1; (str[j] == '.') || (j < str.length() && str[j] >= 48 && str[j] <= 57); j++) {
				if (str[j] == '.') {
					//若为小数
					int k, l;
					for (k = j + 1, l = 1; k < str.length() && str[k] >= 48 && str[k] <= 57; k++, l++) {
						b = float(str[k]) - 48;
						a = a + b * pow(10, -l);
					}
					j = k;
					break;
				}
				else {
					b = float(str[j]) - 48;
					a = a * 10 + b;
				}
			}
			i = j - 1;
			Num.push(a);  //运算数进栈
		}
		else {
			//若为运算符
			char c1 = str[i];  //c1当前运算符
			while (1) {
				char c2 = Sign.top();             //c2栈顶运算符
				if (c2 == '#' && str[i] == '#') { //若遇到起始符,并且str也到达末尾
					Sign.pop();
					break;
				}
				bool t = CheckPrior(c1, c2);     //通过优先级判断是否入栈(c1当前,c2栈中)
				if (t) {
					//同意运算符进栈
					int s = Prior(str[i]);
					if (s == 3) {
						//出现')',优先进行括号内的运算
						while ((c2 = Sign.top()) != '(') {
							a = Num.top();  //取出第一个数
							Num.pop();
							b = Num.top();  //取出第二个数
							Num.pop();
							float r = CalNum(b, a, c2);  //求单项运算的结果
							Sign.pop();
							Num.push(r);    //运算得到的新数进栈
						}
						Sign.pop();
					}
					else {
						Sign.push(c1); //运算符进栈
					}
					break;
				}
				else {
					//不同意运算符进栈,执行运算操作
					a = Num.top();
					Num.pop();
					b = Num.top();
					Num.pop();
					float r = CalNum(b, a, c2); //求单项运算的结果
					Sign.pop();
					Num.push(r);
				}
			}
		}
	}
	return Num.top();
}

 运行结果:

请输入计算式:((1+2*3)-1)/2+1.5*3
计算结果为:7.5
请输入计算式:(1+3)*4)-2
算术表达式错误!
原因:遗漏左括号

总结:实现简易计算器主要运用了栈的数据结构。其中主要的操作是通过遍历算数表达式实现的,所以程序总的时间复杂度约为O(n),n是指算数表达式的长度。对于正在练习栈的小伙伴们来说,简易计算器是很不错的练习项目。

以上是我个人的学习成果,很高兴能与大家分享。文章来源地址https://www.toymoban.com/news/detail-718742.html

到了这里,关于简易计算器(详解用栈实现算术表达式求值)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android开发:kotlin语言实现简易计算器

    输入两个数字,可选加减乘除操作符,并计算显示对应结果 随系统切换语言 可对结果进行四舍五入操作 界面布局:activity_main.xml文件代码 字符定义:string.xml文件代码 逻辑实现:MainActivity.kt 文件代码 方法一(偷懒): 复制文件到对应位置 方法二: 1. 绘制界面 2. 编写逻辑

    2023年04月08日
    浏览(47)
  • 【Servlet学习三】实现一个内存版本的简易计算器~

    目录 一、方式1:使用form表单的形式(不推荐) 🌈1、前端代码:HTML文件 🌈2、后端代码:Calculator_form.java文件 🌈3、最终效果 二、方式2:使用ajax形式(最常用重点!!!) 🌈1、前端代码:HTML文件 🌈2、后端代码:Calculator_ajax.java文件 🌈3、最终效果  注意: (1)前端

    2024年02月12日
    浏览(43)
  • 单片机实现简易计算器功能,附有解析与代码

    目录 首先分为根据要实现的功能来选择硬件和软件: 硬件部分 软件部分 输入部分: 计算部分: 连续计算: 源代码示例: 主函数: 键盘输入: LCD1602显示: 蜂鸣器: 延时函数: 首先我们要实现的功能有:多位显示,小数计算,连续计算,符号按错修改,, 用到LCD1602显示

    2024年02月09日
    浏览(48)
  • Java算法题 给一个字符串表达式,实现一个基本计算器,返回计算结果

    题目: 考点:栈 解题思路: 使用 2 个栈,一个 stack_nums 用来保存计算过程的操作数,一个 stack_symbol 用来保存运算符。 在HashMap中,指定加减优先级为1,乘除优先级为2 循环遍历字符串s, 操作符入栈: 若当前字符为\\\'+\\\', \\\'-\\\', \\\'*\\\', \\\'/\\\', \\\'(\\\' 时,压入运算符栈 stack_symbol, 操作数入

    2024年02月07日
    浏览(48)
  • 4.2 实现基于栈的表达式求值计算器(难度4/10)

    本作业主要考察:解释器模式的实现思想/栈结构在表达式求值方面的绝对优势 C++数据结构与算法夯实基础作业列表 通过栈的应用,理解特定领域设计的关键作用,给大家眼前一亮的感觉。深刻理解计算机语言和人类语言完美结合的杰作。是作业中的上等作品,是数据结构与

    2024年02月10日
    浏览(34)
  • JAVA制作的简易计算器——傻瓜计算器

    用JAVA编写的傻瓜计算器 作用: 1.可以实现加法、减法、乘法、除法简单运算且是单一运算,不可混合使用。 2.CE为清除键 3.没有小数点O(∩_∩)O 思路: 创建JFrame窗口,设置标题,创建JButton,创建文本框JTextField用作显示。 先定义各种按钮类型,用作成员。定义窗口方法对窗口

    2024年02月11日
    浏览(44)
  • JAVA简易计算器

    1.C是清除键,功能是将之前所输入的数字、计算结果等信息全部归零 2.CE,清除当前输入的数据或符号 3.单击MS存储当前显示值,可以理解为放到存储区 4.单击MC清除存储区数值 5.单击MR将存储区数据显示到屏幕上 6.M+:当前显示的数与存储区的数相加 7.M-:当前显示的数与存储

    2024年02月09日
    浏览(50)
  • QT 简易计算器

    2024年02月09日
    浏览(43)
  • java 简易计算器

    1.使用Java图形界面组件设计软件,界面如图所示。 2.软件能够满足基本的“加、减、乘、除”等运算要求。 3.程序代码清晰,语法规范,结构合理,逻辑正确。 先分析,计算器大概是由三个大部分组成的:菜单栏,显示框,按钮。 所以定义一个类cal继承JFrame。 我们定义完后

    2024年02月01日
    浏览(50)
  • 龙芯1b(LS1B200)使用LVGL7.0.1组件 按钮矩阵 实现简易计算器

    需求:通过按钮点击实现加减乘除的简易计算器。 1.我们使用矩阵按钮(lv_ btnmatrix )快速创建布局,文本框( lv_textarea)显示结果 。 2.在按钮回调中将点击结果加入文本框上显示。 矩阵按钮相对于普通按钮: • 对于基于网格的按钮布局,按钮矩阵更易于使用。 • 每个按钮矩阵

    2024年02月05日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包