使用堆栈实现对算数四则混合运算表达式的求值(C语言)


很方便的目录

  • 使用堆栈实现对算数四则混合运算表达式的求值(C语言)
    • 一、问题分析
      • 基本思路
      • 算符优先级构建
    • 二、算法描述
      • 算法描述
      • 堆栈运算过程的直观体现
    • 三、代码实现
      • 源代码
      • 测试数据
    • 四、一些说明

一、问题分析

基本思路

想要用算法来计算一个算术表达式(字符串),要能够正确地解释表达式。首先,明确一下四则运算的基本规则:

  • 先乘除,后加减
  • 从左到右计算
  • 先括号内,后括号外

可以在一边入栈的过程中,一边根据运算规则一步步计算式子。问题在于堆栈如何设置。假设是一个简单的不含括号的四则运算,尝试只使用一个堆栈来解决问题。举个例子:

e.g. 3+4*5-1#(#作为结束符)
1、首先3入栈,+入栈,下一个字符是4,这个时候就遇到一个问题,在不知道下一个字符是乘除还是加减的情况下,不能进行3+4的计算。
2、于是4也入栈,读取下一个字符,发现是 *,于是 * 入栈
3、读取下一个字符5,同时读取栈顶发现是 * ,可以计算,于是 * 出栈,4出栈,4 * 5 得到20并暂存。
4、读取栈顶字符发现是 + ,读取下一个字符,发现是 - ,所以要进行3+20的计算
5、类似,后续计算过程省略

观察上述计算过程,我们发现在判断是否执行一步计算的时候,首先要做的是判断相邻两个运算符的优先级,然后才能决定是否进行计算。这给我们一种启发,设置两个堆栈,运算符堆栈(OPRT)与运算数堆栈(OPND),分别存储运算符(Operator)与运算数(Operand),将使进出栈的步骤简化,判断算符优先级的过程更为容易。

算符优先级构建

我们用> 、=、<来简单表示左算符sym1与右算符sym2对比下的优先级。sym1存在算符栈栈顶,而sym2为刚从字符串中读取到的字符。

  1. sym1>sym2 执行sym1的计算,sym1出栈
  2. sym1<sym2 sym2进栈
  3. sym1=sym2 左右括号相遇,表示括号内计算完成,sym1出栈,sym2清空

列表如下:

sym1\sym2 + - * / ( ) #
+ > > < < < > >
- > > < < < > >
* > > > > < > >
/ > > > > < > >
( < < < < < = EORROR
) > > > > ERROR > >
# < < < < < ERROR =

1、乘除优先级大于加减,同级运算符处于左侧时优先级更大
2、sym1=‘(’ 时,要进行该括号右侧的运算,sym2应当入栈所以sym1<sym2
3、sym2=’(’ 时,要进行该括号右侧的运算,此时sym2应当入栈所以sym1<sym2
4、sym2=’)’ 时,要进行该括号左侧的运算,并使sym1出栈,所以sym1>sym2
5、sym1=‘(’ 且sym2=’)‘时表示括号内运算完成,消去一对括号,所以sym1=sym2
6、一般来说,‘)’ 在括号内计算结束后就被消去了,没有入栈的情况,因此若 ‘)’ 在栈内,应当进行消括号的操作,将消括号的操作定义为 “ ’)’ 的运算”
7、‘#’ ‘#’表示表达式计算完成(在栈底放置’#‘ 用来与结束符对应)
8、几种表达式错误的情况:’)’ num ‘(’ ,’#’ num ‘)’,’(’ num ‘#’ ,在表达式括号不匹配时会出现以上情况,应当报错

二、算法描述

  • 设置两个工作栈,OPTR寄存运算符,OPND寄存运算数或者中间运算结果
  • 首先置操作数栈为空栈,起始符 ’#‘ 作为栈底元素
  • 依次读入表达式字符,运算数进OPND栈,运算符和栈顶元素进行优先级比较后再进行相应操作。
  • OPTR中栈顶元素与当前读入字符均为’#‘时求值结束,结果输出

算法描述

OperandType EvaluateExpression(){//OP为运算符合集
InitStack(OPTR); Push(OPTR,'#');
InitStack(OPND); c=getchar();
while(c!=#||GetTop(OPTR)!='#'){if(!In(c,OP)){Push(OPND,c); c=getchar(); }  //不是运算符则进栈elseswitch(Precede(GetTop(OPTR),c)){case'<': //符号入栈Push(OPTR,c); c=getchar();break;case'=':  //脱括号并接收下一个字符Pop(OPTR,x); c=getchar();break;case'>':  //退栈并将运算结果入栈Pop(OPTR,theta);Pop(OPND,b); Pop(OPND,a);Push(OPND,Operate(a,theta,b));break;} //switch} //whilereturn GetTop(OPND);    //返回运算结果} //EvaluateExpression

函数说明:

InitStack(S) //构造一个空栈
Push(S,e) //入栈
Pop(S,x) //出栈
GetTop(S) //返回栈顶元素
In(c,OP) //比较,判断c是否为OP中的运算符
Precede(c1,c2) //比较两个运算符c1与c2的优先级,并返回> = <
Operate(num1,sym,num2) //执行运算操作,num1、num2为运算数,sym为运算符

堆栈运算过程的直观体现

e.g.3*(7-2)

步骤 OPTR栈 OPND栈 输入字符 主要操作
1 # 3 ‾ \underline{3} 3​ * \text{*} *(7-2) # Push(OPND,’ 3 \text{3} 3’)
2 # 3 ∗ ‾ \underline{*} ∗​(7-2) # Push(OPTR,’ * \text{*} * ')
3 # * \text{*} * 3 ( ‾ \underline{(} (​ 7-2) # Push(OPTR,’ ( \text{(} ( ')
4 # * \text{*} * ( 3 7 ‾ \underline{7} 7​ -2) # Push(OPND,’ 7 \text{7} 7 ')
5 # * \text{*} * ( 3 7 − ‾ \underline{-} −​ 2) # Push(OPTR,’ - \text{-} - ')
6 # * \text{*} * ( - 3 7 2 ‾ \underline{2} 2​ ) # Push(OPND,’ 2 \text{2} 2 ')
7 # * \text{*} * ( - 3 7 2 ) ‾ \underline{)} )​ # Operate(‘7’ , ‘-’ , ‘2’)
8 # * \text{*} * ( 3 5 ) ‾ \underline{)} )​ # Pop(OPTR) {消去一对括号}
9 # * \text{*} * 3 5 # Operate(‘3’ , ‘ * \text{*} *’ , ‘5’)
10 # 15 # return(GetTop(OPND))

三、代码实现

源代码

  • 基本思路同算法描述一致
  • 更改输入方式为从文件输入
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define ERROR -1        //报错返回值
#define TRUE 1
#define OK 0
#define STACK_INIT_SIZE 30      //堆栈初始化分配空间
#define STACKINCREMENT 50       //追加堆栈空间
struct stack{           //堆栈结构体定义int *bottom;int *top;int stacksize;
};int InitStack(stack &S);
int push(stack &S,int ch);
int pop(stack &S,int &ch);
int GetTop(stack &S);
int IsOptr(char ch);
char Precede(int sym1,int sym2);
int Operate(int a,int theta,int b);FILE *fp;int main(){stack OPTR,OPND;    //OPTR堆栈保存运算符,OPND堆栈保存运算数char c='0';         //c为从文件读取的当前字符int x,theta,a,b;    //x作为数值暂存器,theta存运算符,a,b分别为theta运算符前后的运算数fp=fopen("equation.txt","r");while(c!='@'){InitStack(OPTR); push(OPTR,'#');InitStack(OPND); c=fgetc(fp);while(c!='#' || GetTop(OPTR)!='#'){if(!IsOptr(c)){     //是否运算符,若不是则进行运算数压栈push(OPND,c-48);c=fgetc(fp);while(!IsOptr(c)){      //多于一位字符的运算数需要多次处理pop(OPND,x);push(OPND,x*10+c-48);c=fgetc(fp);}}elseswitch(Precede(GetTop(OPTR),c)){        //根据运算符优先级选择操作case'<':push(OPTR,c);c=fgetc(fp);break;     //运算符压栈case'=':pop(OPTR,x);c=fgetc(fp);break;      //运算符出栈case'>':pop(OPTR,theta);                    //执行一次计算pop(OPND,b);pop(OPND,a);push(OPND,Operate(a,theta,b));break;}}printf("%d\n",GetTop(OPND));        //打印一个算术表达式的结果c=fgetc(fp);        //读取算术表达式后的字符,分回车符'\n'和文件结束符'@'两种情况}fclose(fp); return 0;
}int InitStack(stack &S){               //堆栈初始化S.bottom=(int *)malloc(STACK_INIT_SIZE*sizeof(char));if(!S.bottom) exit(OVERFLOW);S.top=S.bottom;S.stacksize=STACK_INIT_SIZE;return OK;
}int push(stack &S,int ch){            //压栈if(S.top-S.bottom>=S.stacksize){S.bottom=(int*)realloc(S.bottom,S.stacksize+STACKINCREMENT*sizeof(int));if(!S.bottom) exit(OVERFLOW);S.top=S.bottom+S.stacksize;S.stacksize+=STACKINCREMENT;}*S.top=ch;S.top=S.top+1;return OK;
}int pop(stack &S,int &ch){             //出栈if(S.top==S.bottom) return ERROR;ch=*(S.top-1);S.top--;*(S.top)=0;return OK;
}int GetTop(stack &S){          //返回栈顶元素int ch;if(S.top==S.bottom) return ERROR;ch=*(S.top-1);return ch;
}int IsOptr(char ch){                    //判断是否算符int i=0;char optr[]={'+','-','*','/','(',')','#','@'};while(optr[i]!='@'){if(ch==optr[i]) return TRUE;i++;}return OK;
}char Precede(int sym1,int sym2){          //算符优先级判别if     (sym1=='#'&&sym2=='#') return '=';else if(sym1=='#'&&sym2==')') exit(ERROR);else if(sym1=='#')            return '<';else if(sym1=='('&&sym2=='#') exit(ERROR);else if           (sym2=='#') return '>';else if(sym1==')'&&sym2=='(') exit(ERROR);else if(sym1==')')            return '>';else if(sym1=='('&&sym2==')') return '=';else if           (sym2==')') return '>';else if(sym1=='(')            return '<';else if           (sym2=='(') return '<';else if(sym1=='*'||sym1=='/'||sym2=='+'||sym2=='-') return '>';else return '<';
}int Operate(int a,int theta,int b){         //四则运算操作if(theta=='+') return a+b;else if(theta=='-') return a-b;else if(theta=='*') return a*b;else if(theta=='/'){if(b==0) {printf("该行除数为0,出错!"); return NULL;}else return a/b;} else exit(ERROR);
}

测试数据

“equation.txt”中填入如下的测试数据:

3*(7-2)#
8#
1+2+3+4#
88-15#
1024/4
8#
(20+2)(6/2)#
3-3-3#
8/(9-9)#
2
(6+2*(3+6*(6+6)))#
(((6+6)*6+3)*2+6)*2#@

每行写一串表达式,以 ‘#‘ 结尾 ,’@'为文件结束符

测试结果如下:

[Running] cd “d:\applications\VS_Code\CProFile” && g++ Arithmetic.cpp -o Arithmetic && "d:\applications\VS_Code\CProFile"Arithmetic
15
8
10
83
2048
66
-3
该行除数为0,出错!0
312
312
[Done] exited with code=0 in 1.935 seconds

四、一些说明

  • 用逐个字符的方式读取文件,当整数有不止一位时,采用栈顶数乘十加当前读取位的方式保存完整数
  • 本代码没有实现小数计算,若要实现此功能,需要加入小数点的判别
  • 本代码实现带括号加减乘除的计算,若需要增加运算功能,如乘方、单目增、单目减等,需要在判断是否算符、算符优先级判别以及运算操作函数中再做增改
  • 算法描述搬运自(《数据结构(C语言版)》严蔚敏,吴伟民)QwQ

使用堆栈实现对算数四则混合运算表达式的求值(C语言)相关推荐

  1. 算数四则混合运算表达式求值

    算数混合四则运算求值 [问题] 利用算符优先关系,实现对算术四则混合运算表达式的求值 [要求] 输入的形式:表达式,例如2*(3+4) 包含的运算符只能有'+' .'-' .'*' .'/' .'(' ...

  2. 四则混合运算表达式分析程序的原理及其实现

    意义: 四则混合运算表达式可以看作一定语言中的表达式分析及求值,虽然它很小,却是一个语法分析的很好的例子! 一.目标:可以对输入的四则混合运算表达式进行分析,并求出其结果.程序须支持:整数及小数运算. ...

  3. C语言编程学习:写的秒速计算四则混合运算项目

    C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现 ...

  4. 【数据结构】支持四则混合运算的计算器(转)

    1.给出两个数,用户再指定操作符,要求计算结果,这实现起来很容易:   2.多个数,但只涉及同一优先级的操作符,做起来也很容易:   3.多个数,不同优先级的操作符,怎么办呢? 想想就头痛,不过还好前 ...

  5. 分数混合运算简便方法_分数四则混合运算

    第一课时 分数四则混合运算 教学内容:教科书第80页的例1."练一练",练习十五第1-5题. 教学目标: 1.使学生结合解决实际问题的过程,理解并掌握分数四则混合运算的运算顺序,并 ...

  6. java简单运算程序_JAVA实现简单四则混合运算

    JAVA实现简单四则混合运算,说明:该计算器支持实则混合运算,如 2*(3+1 )/ 4-3 *9+ 8/ 3*4- 5,则输出:-19.333332 需要说明的事括号必须是英文的.源码如下仅供学习: ...

  7. 软件工程学习之小学四则混合运算出题软件 Version 1.00 设计思路及感想

    对于小学四则混合运算出题软件的设计,通过分析设计要求,我觉得为了这个软件在今后便于功能上的扩充,可以利用上学期所学习的<编译原理>一课中的LL1语法分析及制导翻译的算法来实现.这样做的好处 ...

  8. java 四则混合运算_Java实现四则混合运算代码示例

    使用栈来实现,可以处理运算优先级. 使用自然四则运算表达式即可,如:4+(3*(3-1)+2)/2.无需把表达式先转换为逆波兰等形式. package com.joshua.cal; import j ...

  9. c# 四则混合运算算法

    对于四则混合运算其实在大三的时候就接触过,但是由于当时自己太菜,只顾着玩游戏跳课了.所以这个算法一直没有用代码实现过.当时学霸还给我简单的讲了一下方法,只因身为学渣的我,只想对学霸说一句,道理我都懂, ...

最新文章

  1. python简单入门代码-Python入门 | IDLE的介绍和使用方法
  2. Win10系列:JavaScript图形
  3. 可视化mysql怎么导入sql文件_使用navicat导入sql文件的方法步骤
  4. java鼠标进入高亮效果_鼠标选中文本划词高亮、再次选中划词取消高亮效果
  5. 再也不学AJAX了!(二)使用AJAX
  6. 使用taro命令(taro convert)转h5碰到的一些问题
  7. Ubuntu下Authentication token manipulation error或者Authentication Failure解决办法
  8. 学习php技巧,对初学者非常有用的PHP技巧
  9. ***站长自述挂马经历 提醒挂马者回头是岸
  10. 用 Python 和 werobot 框架开发公众号
  11. c语言每条代码的含义,为我解释一下,代码的意思。谢谢了。
  12. 【运维程序】简单的命令控制器(支持定时命令执行、重复定时任务命令和进程管理,开发这个小程序主要是为了方便管理服务进程)【个人github项目】...
  13. 零基础带你学习计算机网络—(四)
  14. java求sin函数咋写_5类“隐含条件”,题干不写但是你要会用(解三角形知识整合,建议收藏)| 真题精讲-16...
  15. 数独用计算机控制比数学家还厉害,用pl/sql解决芬兰数学家因卡拉设计的最难数独...
  16. 云服务优缺点_云服务器优点和缺点
  17. Python:使用nltk统计词频并绘制统计图
  18. WiFi温湿度传感器开发
  19. NatApp免费实现内网穿透
  20. Clustering by Passing Messages Between Data Points(Brendan J.Frey* and Delbert Dueck)例子

热门文章

  1. setLayout()和GridData的设置
  2. vba数组如何精确筛选_v34.VBA数组知识点76问(七)
  3. MaskedTextBox 控件
  4. pcie equalization学习
  5. svm matlab 画图,SVM简单代码实现MATLAB
  6. springmvc运行时,Failed to read candidate component class;nested exception is java.lang.IllegalArgument
  7. scrollTop如何获取
  8. vue解决element-ui popover点击取消时 popover的显示与隐藏问题
  9. 【Pytorch】backward()简单理解
  10. cgb2007-京淘day10