表达式求值是栈这种数据结构的一个很经典的应用,恰逢是数据结构期末实践题目,经过一定的努力终于也是实现了这个算法,所以分享下我的思路和经验,希望正准备尝试解决这个问题的同学可以少走些弯路,有所借鉴和收获。

个人水平有限,文笔拙劣,有所意见和建议欢迎指出

我们日常使用的四则运算式如 1+1 被称为中缀表达式,即所有的运算符号都在运算数的中间出现,我们在初学编程时都练习过简单的二元运算,但当求值问题扩大到一个完整的四则运算式的时候,就需要考虑运算符优先级、括号等多个问题,解决了这些问题也就实现了表达式求值。

表达式求值问题常用的也是最广泛的解决方法是逆波兰式转换法,主要步骤可简单分为两步,用户输入的中缀表达式转化为后缀表达式(逆波兰式),再对逆波兰式进行运算。但在编程过程中我发现简单的分为两步是行不通的,因为涉及到多位数和浮点数,由于输入时统一存储为字符串,在进行中缀到后缀的转换后无法得到正确的后缀式字符串,由于c语言中char型数据实际上也是int型数据,这就进一步增加了正确转换的难度,从而也就无法在逆波兰式的计算中得出正确的结果,所以在编程实现时将这两步合为一个函数实现,即以字符串形式存储键盘输入的表达式后,对该字符串逐位处理,通过ASCII码值的判断分为数字或小数点、运算符两种情况,然后按照判断结果执行相应的语句。

准备工作

创建一char型数组用于存储输入的中缀表达式,在中缀表达式向后缀表达式的转化以及后缀表达式的计算中(详情见各种数据结构教材或书籍,或相关博客,实现此算法必须要实现了解这些过程)我们知道,因为涉及到运算符优先级的判断和后缀表达式的计算的部分我们需要借助栈来实现,所以需要创建一double型栈和一char型栈,分别用于存储操作数和运算符,栈的存储可随意选用线性结构或链式结构,需要实现以下操作函数:

  • 初始化
  • 出栈
  • 入栈
  • 取栈顶
  • 判空

接下来对如何处理输入的表达式做详细介绍

1.数字或小数点(非运算符)

上图为ASCII码对照表

通过ASCII码我们可以得出输入的是数字(0-9)的条件,但因为存在多位数的情况,所以当前数字可能是输入的操作数的十位或百位等,所以不能简单的压入数字栈就完事,我的解决方法是每当遇到数字位都进行一次判断,检查表达式字符串中它的前一位是否为数字,若是,则说明这是个多位数,则将前一位(已入栈)出栈与10的n次幂做乘积后与当前位求和再入栈;对于例如12.34这类浮点数,则在判断当前位不是运算符后首先检查是否为小数点,若是则说明直到下一个运算符都是该浮点数的小数位,则与求多位整数(例12.34的整数位)值类似,与10的负n次幂做乘积累加求和知道遇到下一个运算符时入栈。

上图是我做的流程图(第一次做,略丑,希望能帮助理解即可)

2.运算符

运算符的处理相对于操作数就简单许多了,唯一的小问题就是运算符优先级的判断,我第一次写的时候写了层层嵌套的if和switch语句,做了简化修改后可以这样写:

char Precede(char c1,char c2){//判定运算符的栈顶运算符与读入的运算符之间优先关系char c;switch(c1){case '+':case '-':switch(c2){case '+':case '-':case ')':case '#':c='>';break;default:c='<';}break;case '*':case '/':if(c2=='('){c='<';}else{c='>';}break;case '(':if(c2==')'){c='=';}else{c='<';}break;case ')':c='>';break;case '#':if(c2=='#'){c='=';}else{c='<';}}return c;
}

解决了优先级的判断问题后,根据中缀转后缀的步骤,优先级高于栈顶入栈,低的出栈运算直到栈顶元素优先级低于当前位,思路如下:

如上所述,对表达式字符串处理完毕后,此时运算结果其实就是操作数栈的栈顶元素,且操作数栈仅剩该数字,输出即可。

下面是完整代码和流程图,如果文字描述不足以让你彻底明白这个问题的解决方法,希望能静下心多看看代码,愿你能保持学习的热情

//zhao6582@qq.com 12/18
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define MAXSIZE 100
//double栈,用于存放运算数
typedef struct {double data[MAXSIZE];int top;
}Stack_n;
//char栈,用于存放运算符
typedef struct {char data[MAXSIZE];int top;
}Stack_c;
//栈的初始化
void Init_n(Stack_n s);
void Init_c(Stack_c s);
栈的判空函数
int IsEmpty_n(Stack_n s);
int IsEmpty_c(Stack_c s);
//入栈函数
void Push_n(Stack_n *s,double e);
void Push_c(Stack_c *s,char e);
//出栈函数
void Pop_n(Stack_n *s,double *e);
void Pop_c(Stack_c *s,char *e);
//取栈顶函数
double GetTop_n(Stack_n s);
char GetTop_c(Stack_c s);char Precede(char c1,char c2);//判断优先级
double Operate(double a,char theta,double b);//二元运算
int In(char c,char *OP);//是否为 + - * / ( )
void check(char *s);//用于检查输入的多项式格式是否正确
double Poland(char * s);//主要功能实现函数char OP[7]={'+','-','*','/','(',')','#'}; //运算符数组(因多个函数使用故声明为公有的)int main(){char s[100];printf("请输入表达式:\n");gets(s);double r = Poland(s);printf("%1.2lf\n",r);return 0;
}void Init_n(Stack_n *s){s->top = 0;
}void Init_c(Stack_c *s){s->top = 0;
}
//栈的判空函数
int IsEmpty_n(Stack_n s){if(s.top == 0){return 1;}else{return 0;}
}
int IsEmpty_c(Stack_c s){if(s.top == 0){return 1;}else{return 0;}
}
//入栈函数
void Push_n(Stack_n *s,double e){s->top++;s->data[s->top] = e;
}
void Push_c(Stack_c *s,char e){s->top++;s->data[s->top] = e;
}
//出栈函数
void Pop_n(Stack_n *s,double *e){*e = s->data[s->top];s->top--;
}
void Pop_c(Stack_c *s,char *e){*e = s->data[s->top];s->top--;
}
//取栈顶函数
double GetTop_n(Stack_n s){return s.data[s.top];
}
char GetTop_c(Stack_c s){return s.data[s.top];
}char Precede(char c1,char c2){//判定运算符的栈顶运算符与读入的运算符之间优先关系char c;switch(c1){case '+':case '-':switch(c2){case '+':case '-':case ')':case '#':c='>';break;default:c='<';}break;case '*':case '/':if(c2=='('){c='<';}else{c='>';}break;case '(':if(c2==')'){c='=';}else{c='<';}break;case ')':c='>';break;case '#':if(c2=='#'){c='=';}else{c='<';}}return c;
}double Operate(double a,char theta,double b){//进行二元运算 a theta bdouble sum;switch(theta){case '+':sum=a+b;break;case '-':sum=a-b;break;case '*':sum=a*b;break;case '/':sum=a/b;break;}return sum;
}int In(char c,char *OP){//判断是不是运算符for(int i=0;i<7;i++)if(c==OP[i])   //是运算符return 1;return 0;
}
//判断思路:当前字符是运算符且前一位或后一位也是运算符且这3位都不为‘(’和‘)’时说明连续输入了两个运算符
void check(char *s){char *t = s;for(int i = 0;i < strlen(s);i++){if(i > 0 && i < strlen(s) - 1 && In(s[i],OP) && (In(s[i - 1],OP) || In(s[i + 1],OP))&&(s[i] != '(' && s[i] != ')') && (s[i - 1] != '(' && s[i - 1] != ')')&&(s[i + 1] != '(' && s[i + 1] != ')')){printf("输入格式有误!!请检查是否存在连续输入了运算符等错误!!\n");getchar();exit(0);}}
}double Poland(char * s){//对多项式格式进行检查check(s);//进行多项式的求值以及后缀表达式的输出int i=0,len,flag=0;double a,b,sum;char c1=s[0],e;Stack_c OPTR;     //运算符栈Stack_n OPND;     //运算数栈Init_c(&OPTR);  //初始化栈Init_n(&OPND);len=strlen(s);s[len]='#';s[len+1]='\0';Push_c(&OPTR,'#');//补‘#’作为结束标志while(s[i]!='#'||GetTop_c(OPTR)!='#'){ //遍历每一个字符 为‘#’结束循环if(s[i] == ' '){i++;continue;}if(!In(s[i],OP)){ //如果不是运算符if(c1=='.'){//如果上一个字符是小数点flag++;}if(flag){double t;Pop_n(&OPND,&t);Push_n(&OPND,t + (double)(s[i] - '0') / pow(10,flag));//对小数点后的部分 按位数运算并入栈 flag++;}if(s[i]!='.'&&!flag){if(!In(c1,OP)&&!IsEmpty_n(OPND)){//如果上一个字符是数字(说明是多位数) 根据位数运算后入栈double t;Pop_n(&OPND,&t);Push_n(&OPND,t * 10 + s[i] - '0');}else//否则压入运算数栈Push_n(&OPND,(double)(s[i]-'0'));}c1=s[i++]; //读取下一个字符}else{//如果是运算符flag=0;switch(Precede(GetTop_c(OPTR),s[i])){case '>':  //当前运算符优先级低Pop_c(&OPTR,&e); //运算符出栈,和操作数的头两个数运算 Pop_n(&OPND,&b);Pop_n(&OPND,&a);Push_n(&OPND,Operate(a,e,b)); //计算结果入栈break;case '=': //优先级相等Pop_c(&OPTR,&e); //弹出运算符栈顶元素c1 = s[i++];break;case '<':Push_c(&OPTR,s[i]); //当前运算符优先级高 入栈c1 = s[i++];break;}}}sum=GetTop_n(OPND);return sum; //返回运算数栈顶元素,即运算结果
}//@@ end @@

代码也是看了很多博客帖子后总结出来的,建议好好利用搜索引擎。

栈的实现、逆波兰转换和计算等基本概念很多书都有,我看的是学校教材,清华大学出版社出版、严蔚敏老师的《数据结构(C语言版)》和程杰老师的《大话数据结构》。

(C语言实现)栈求表达式的值(实数范围内)相关推荐

  1. 利用栈求表达式的值_高一数学月考考点之函数的表达式详解

    函数表达式考点详解,教你轻松学函数 Hello,大家好,这里是摆渡学涯.很高兴在这里跟大家分享知识哦.这次课程我们来为大家讲一下函数表达式相关的考点,教你轻松学函数. 基本概念 什么是函数表达式呢?我 ...

  2. 【勇敢牛牛,不怕困难】有手就行栏目:头歌教学平台 - 湖南工业大学刘强老师的C语言函数实战课堂作业答案 - > - > {求和+回文数计算+编写函数求表达式的值+阶乘数列+亲密数+公约数公倍数求解}

    第一关:求和 任务描述 题目描述:给你一个n,要求你编写一个函数求1+2+-+n. 输入 输入一个n 输出 输出1+2+-+n的和 测试说明 样例输入: 100 样例输出: 5050 分析: 这个是真 ...

  3. EduCoder-程序设计技术R-函数-(第1关:求和)(第2关:回文数计算)(第3关: 编写函数求表达式的值)(第4关:阶乘数列)(第5关:亲密数)(第6关:公约公倍数)

    目录 第1关:求和 代码示例 第2关:回文数计算 代码示例 第3关: 编写函数求表达式的值 代码示例 第4关:阶乘数列 代码示例 第5关:亲密数 代码示例 第6关:公约公倍数 代码示例 第1关:求和 ...

  4. C语言-用栈实现表达式求值

    目录 目的描述: 算法的基本思想: 错误点: 完整代码: 1.输入输出 2.栈操作函数包(数组堆栈.h) 3.实现表达式求值函数包(表达式求值.c) 4.测试输出: 目的描述: 算符优先算法要实现的是 ...

  5. c语言写程序计算表达式的值,C语言 写的 表达式求值。

    有不对的地方还望指出来,让我改正.谢谢.存一个代码 #include #include #include #include #define Stack_Size 1010 #define INF 21 ...

  6. LeetCode Basic Calculator(用栈计算表达式的值)

    题意:给出一个计算表达式,只包含 +,-,(,),求计算结果 思路:用栈来实现 代码如下: public class Solution {private int cal(int num1, int n ...

  7. 链表练习(一元多项式):一元多项式采用带表头结点的单链表存放,用类C语言设计算法求一元多项式的值。

    注意嗷:这里的一元多项式输入时,指数exp不一定是按照递增顺序输入的,但是最终我们想获得一个指数递增的链表形式,所以在插入新结点时,需要进行判断,把他放在合适的位置. #include<stdi ...

  8. 顺序栈实现表达式求值(C语言实现)【栈】

    原理说明 代码实现 原理说明 表达式求值一定会出现表达式中运算符的优先级问题. 运算规则: 先乘除,后加减: 从左算到右: 先括号内,后括号外: 运算符优先表: 上面表格中有一些比较特殊的位置: ① ...

  9. c语言如何求一个数学表达式的值,浅谈C语言中表达式的求值

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 C语言研究性学习的路线 现行的多数C语言教材有太多的误区,不仅不能给读者提供有效的学习线索,还常常"误导"读者,于是,"死记 ...

  10. 《Algorithms》—— Dijkstra 的双栈算术表达式求值算法

    想当年学数据结构的时候,一直觉得这个是我一辈子都搞不懂的一个东西.现在看看...还挺简单的... 重点在于如何解析由括号.运算符和数字组成的字符串,并按照正确的顺序完成各种初级算术操作.利用了两个栈( ...

最新文章

  1. numpy.random.normal详解
  2. 简单io应用—流水灯控制_制作简单有趣的可调速流水灯
  3. ref与out一看就懂
  4. lintcode:递归打印数字
  5. Leetcode —— 886. 可能的二分法
  6. 使用carbon_东华大学《Carbon》多孔碳纳米纤维复合膜,优异电磁波吸收性能!
  7. 服务器系统怎么写,服务器操作系统语言写的
  8. 如何实现公平的效绩考核?
  9. gdb 调试带参数的程序-转
  10. 【特别版】计算机哲学对学习生活借鉴的几个例子
  11. HTML网页设计期末课程大作业~旅游住宿酒店的HTML网页设计(14页) ~学生网页设计作业源码 旅游网页代码 学生网页设计与制作期末作业下载 DW旅游网页作业代码下载...
  12. 内网通手动修改积分_WooCommerce微信小程序2.9.4版本发布 拼团积分购适配可变产品 微信搜索推送...
  13. jflash烧录教程_Jflash烧录(windows)原理分析
  14. (已更新)日常记账微信小程序模板源码
  15. 发表skiller的几个版本
  16. 解决Veil—Evasion安装中git clone导致失败的问题
  17. android程序设计拍照,Android编程实现拍照功能的2种方法分析
  18. 订单中心项目分析与总结
  19. 使用激光雷达创建地图
  20. 修改 Docker 镜像默认存储位置的方法

热门文章

  1. 如何将caj转换成word?caj转Word工具
  2. 微信关注公众号获取用户名的方法
  3. python编写库存管理_python编写商品管理
  4. Imagine中文文档
  5. 华为云--欧拉操作系统
  6. 大一新生HTML期末作业 个人网页王嘉尔明星介绍网页设计与制作
  7. Android软键盘弹不出的问题
  8. 联想如何打开计算机配置,联想电脑如何进入BIOS以及设置启动菜单
  9. Laya 实现一个轻量高效好用的Tween库并支持Laya.Ease方法
  10. MCU控制继电器的电路详解