这里要学的程序主要用来实现一个功能——输入表达式输出结果,也就是一个计算器。效果如下:

这个程序主要有两个步骤:1、把中缀表达式转换为后缀表达式;2、计算后缀表达式的结果。

首先先明白几个问题:

1、为什么要转换为后缀表达式?因为后缀表达式容易实现计算机计算结果。(可以百度一下后缀表达式,又称逆波兰式)

2、怎么把中缀表达式转换为后缀表达式?

3、怎么用后缀表达式输出结果?

相信如果弄明白了上面几个问题,有C语言基础的同学就可以编出这个程序啦。而后面两个问题都要用到一个数据结构——栈。实际上数据结构只是一种思想,一种思维,是连接人脑与计算机的桥梁(纯属个人杜撰= =)

  好,那么我们先学怎么把中缀表达式转换为后缀表达式。

  为了简化问题,我们不妨设输入的数字都是一位整数,这样读入的时候只需要以字符的方式逐个读入即可,也就是说读入的必定是数字或者+-*/,读到回车视为结束;另外不妨直接将结果打印到屏幕上,也就是说结果不必存储(这样我们可以更多地关注算法本身,而非输入输出方式的细枝末节)

  类似上面的图片,它的后缀表达式是  9 3 1 - 2 * + 5 2 / +  怎么得到的呢?下面开始讲这个转换的算法:

  1、设置一个栈,栈底填一个"@"作为结束符(当然也可以用别的符号,至于为什么要用一会讲优先级的时候会说);

  2、开始逐个字符读入;

  3、如果读入的是数字,直接打印printf("%c ",c);(数字后面打印一个空格作为间隔符,要不然没法看了931-。。。不解释)

  4、如果读入的是"("直接进栈;

  5、如果读入的是")",说明前面肯定读入过一个"("找到这个左括号,把两者之间的符号逐个弹栈(这里要说明的是,括号不必打印,因为后缀表达式没有括号);

  6、如果不是上面的几种情况,那必定是+-*/中的一个啦,这样来说就容易多了。本来这里应该说优先级的问题,可是我还是想先说说栈。讲到这里,实际上大家应该也看出来了,栈实际上就是一个进栈和出栈的问题,这里也是一样,对于表达式这里我们可以发现,这个栈是用来存储符号的,()+-*/,所以我们只需要明白什么时候符号可以进栈,什么时候符号可以出栈就可以啦~上面已经讲了两个了,左括号的时候直接可以进栈,右括号的时候把两者之间的出栈打印。而对于+-*/,只需要记住一个法则,对于读入的这个符号,只有它比栈顶符号的优先级高的时候才可以进栈(优先级相同也不能进栈),而它不能进栈,就只能让栈顶的出栈啦~所以不断出栈,知道这个符号可以进栈,这个新读入的符号就算处理完成啦。(优先级函数可以参照后面的程序代码)

OK,按照上面的算法,扫描完一遍读入的中缀表达式,就可以在屏幕上输出后缀表达式啦。下面附上自制代码(C语言,带注释):

 #include<stdio.h>
#include<stdlib.h>
#define newp (stype *)malloc(sizeof(stype)) //定义一个申请栈地址的宏
typedef struct _stack{
char dat;
struct _stack *next;
} stype; //建立栈类型
int tance(char x) //探测优先级
{
if(x=='+'||x=='-') return ;
else if (x=='*'||x=='/') return ;
else if (x=='@'||x=='('||x==')') return -;
}
int main()
{
stype *s,*top; //栈指针和栈顶指针
char c;
s=newp;
s->dat='@';
s->next=NULL;
top=s;
c=getchar(); //此后为读取中缀表达式的部分,用字符一个一个的读,直到读到回车
while(c!='\n')
{
if (c>=''&&c<='') //如果读入数字,直接打印
{
printf("%c ",c);
}
else if (c=='(') //如果是左括号,直接进栈
{
s=newp;
s->dat=c;
s->next=top;
top=s;
}
else if (c==')') //如果是右括号,匹配左括号,把两者之间的栈内符号全部弹出
{
while (top->dat!='(')
{
s=top;
printf("%c ",top->dat);
top=top->next;
free(s);
}
s=top;
top=top->next;
free(s);
}
else //否则肯定是+-*/了
{
int a=tance(c);
int b=tance(top->dat); //比较该符号和栈顶符号的优先级
if (a>b) //如果大于直接压进去
{
s=newp;
s->dat=c;
s->next=top;
top=s;
}
else //否则就把栈顶的符号一直弹出,直到弹到可以压进去,然后压进去(也就是说等于也不能压进去)
{
while (a<=b)
{
s=top;
printf("%c ",top->dat);
top=top->next;
free(s);
b=tance(top->dat);
}
s=newp;
s->dat=c;
s->next=top;
top=s;
}
}
c=getchar(); //读取下一个字符
}
while (top->dat!='@') //读完和还不算完,还要把栈内剩余的所有符号挨个弹出
{
s=top;
printf("%c ",top->dat);
top=top->next;
free(s);
}
return ; //后缀表达式输出完毕
}

好,如果你看懂了上面所有的内容,恭喜你,已经学会一半了。那么,现在我们就可以去掉那个只能输入一位数的大前提,只需要在输入的时候处理一下,就可以实现任意位数的数,甚至是小数(可以参照最后的程序代码)。

那么现在我们来完成第二步:已知后缀表达式输出结果。这个算法要比上面的转换简单多了,就是栈的基本操作,只不过这里的栈不是用来存储字符的,而是用来存储数字的。

我们不妨还是假设一位数吧。

1、如果读入的是数字,直接进栈;

2、如果是符号,必然是+-*/中的一个,直需要弹出栈顶的两个数,运算,然后再把结果进栈。直至扫描完整个后缀表达式,栈顶就是最终结果。

如果看懂了上面,我们可以发现,这个过程直接就可以在转换表达式的时候顺便完成,也就是,如果遇到数字,不打印到屏幕上,而是进栈到数字存储栈里;如果有出栈的符号,不用打印到屏幕上,而是弹出数字栈的两个栈顶元素,然后进栈。就OK啦。下面附上输入中缀表达式输出结果的代码(只是将上面的代码中打印的过程换成了其他操作而已):

 #include<stdio.h>
#include<stdlib.h> #define newp (stype *)malloc(sizeof(stype)) //定义一个申请栈地址的宏 typedef struct _stack{
char dat;
struct _stack *next;
} stype; //建立栈类型 int tance(char x) //探测优先级
{
if(x=='+'||x=='-') return ;
else if (x=='*'||x=='/') return ;
else if (x=='@'||x=='('||x==')') return -;
} int main()
{
int rs=;
stype *s,*top; //栈指针和栈顶指针
int calc[],i=;
char c;
s=newp;
s->dat='@';
s->next=NULL;
top=s;
c=getchar(); //此后为读取中缀表达式的部分,用字符一个一个的读,直到读到回车
while(c!='\n')
{
if (c>=''&&c<='') //如果读入数字,直接打印
{
i++;
calc[i]=c-;
}
else if (c=='(') //如果是左括号,直接进栈
{
s=newp;
s->dat=c;
s->next=top;
top=s;
}
else if (c==')') //如果是右括号,匹配左括号,把两者之间的栈内符号全部弹出
{
while (top->dat!='(')
{
s=top;
if (top->dat=='+'){
calc[i-]=calc[i-]+calc[i];
i--;
}
else if (top->dat=='-'){
calc[i-]=calc[i-]-calc[i];
i--;
}
else if (top->dat=='*'){
calc[i-]=calc[i-]*calc[i];
i--;
}
else if (top->dat=='/'){
calc[i-]=calc[i-]/calc[i];
i--;
} top=top->next;
free(s);
}
s=top;
top=top->next;
free(s);
}
else //否则肯定是+-*/了
{
int a=tance(c);
int b=tance(top->dat); //比较该符号和栈顶符号的优先级
if (a>b) //如果大于直接压进去
{
s=newp;
s->dat=c;
s->next=top;
top=s;
}
else //否则就把栈顶的符号一直弹出,直到弹到可以压进去,然后压进去(也就是说等于也不能压进去)
{
while (a<=b)
{
s=top;
if (top->dat=='+'){
calc[i-]=calc[i-]+calc[i];
i--;
}
else if (top->dat=='-'){
calc[i-]=calc[i-]-calc[i];
i--;
}
else if (top->dat=='*'){
calc[i-]=calc[i-]*calc[i];
i--;
}
else if (top->dat=='/'){
calc[i-]=calc[i-]/calc[i];
i--;
}
top=top->next;
free(s);
b=tance(top->dat);
}
s=newp;
s->dat=c;
s->next=top;
top=s;
}
}
c=getchar(); //读取下一个字符
}
while (top->dat!='@') //读完和还不算完,还要把栈内剩余的所有符号挨个弹出
{
s=top;
if (top->dat=='+'){
calc[i-]=calc[i-]+calc[i];
i--;
}
else if (top->dat=='-'){
calc[i-]=calc[i-]-calc[i];
i--;
}
else if (top->dat=='*'){
calc[i-]=calc[i-]*calc[i];
i--;
}
else if (top->dat=='/'){
calc[i-]=calc[i-]/calc[i];
i--;
}
top=top->next;
free(s);
} printf("%d\n",calc[]);
return ;
}

OK,会了一位数的,其他的也就是小case啦,如果想看完整的多位数的代码,可以参看  栈的应用2——超级计算器(中缀与后缀表达式)C语言

栈的应用1——超级计算器(中缀与后缀表达式)C语言的更多相关文章

  1. C++ 使用栈求解中缀、后缀表达式的值

    1. 前言 表达式求值对于有知识积累的你而言,可以通过认知,按运算符的优先级进行先后运算. 但对计算机而言,表达式仅是一串普通的信息而已,需要通过编码的方式告诉计算机运算法则,这个过程中栈起到了至关重 ...

  2. 前缀、中缀、后缀表达式及其相互转化的Java实现

    一.中缀表达式转换为前缀.后缀表达式 给个中缀表达式:a+b*c-(d+e)    首先根据运算符的优先级给所有运算单位加括号:((a+(b*c))-(d+e))    将运算符号移动到对应括号的前面 ...

  3. Java数据结构和算法(六)——前缀、中缀、后缀表达式

    前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...

  4. C++ 中缀转后缀表达式并求值

    //中缀转后缀 #include<iostream> #include<stack> using namespace std; int prio(char x){ ; ; ; ...

  5. Java数据结构和算法(六):前缀、中缀、后缀表达式

    前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...

  6. 数据结构之栈—强大的四则复杂运算计算器(超过windows自带的科学计算器)【中缀转后缀表达式】

    比windows自带计算器还强的四则复杂运算计算器! 实测随机打出两组复杂算式:-7.5 * 6 / ( -2 + ( -6.5 -  -5.22 ) )与7.5+-3*8/(7+2) windows ...

  7. 6, java数据结构和算法: 栈的应用, 逆波兰计算器, 中缀表达式--> 后缀表达式

    直接上代码: public class PolandCalculator { //栈的应用:波兰计算器: 即: 输入一个字符串,来计算结果, 比如 1+((2+3)×4)-5 结果为16 public ...

  8. 前缀、中缀、后缀表达式以及简单计算器的C++实现

    前缀表达式(波兰表达式).中缀表达式.后缀表达式(逆波兰表达式) 介绍 三种表达式都是四则运算的表达方式,用以四则运算表达式求值,即数学表达式的求解. 前缀表达式 前缀表达式是一种没有括号的算术表达式 ...

  9. 栈的简单应用之中缀表达式转后缀表达式(C语言实现逆波兰式)

    一.前言   普通人在书写计算式时会选择中缀表达式,这样符合人脑的认知习惯.可计算机处理时后缀表达式才能使处理速度更快,其原因是利用堆栈结构减少计算机内存访问.同时它也是一个很好锻炼栈这个数据结构的应 ...

随机推荐

  1. uva 10054 The Necklac(欧拉回路)

    明显的欧拉回路,把颜色作为点,建图后,做一遍欧拉回路.不过我是现学的,打印路径上纠结了一下,发现随着FindEuler()的递归调用的结束,不断把点压入栈中,从后向前打印,遇到"支路&quo ...

  2. java读取照片信息 获取照片拍摄时的经纬度

    项目结构 源码:ImageInfo.zip 第一步:添加需要的架包metadate-extractor.jar 架包下载地址:https://code.google.com/p/metadata-ex ...

  3. Web API入门指南(安全)转

    安全检测的工具站点:https://www.owasp.org/index.php/Category:Vulnerability_Scanning_Tools Web API入门指南有些朋友回复问了些 ...

  4. 最简单的视音频播放示例4:Direct3D播放RGB(通过Texture)

    本文接着上一篇文章继续记录Direct3D(简称D3D)播放视频的技术.上一篇文章中已经记录了使用Direct3D中的Surface渲染视频的技术.本文记录一种稍微复杂但是更加灵活的渲染视频的方式:使 ...

  5. TCP/IP详解学习笔记(3)-IP协议,ARP协议,RARP协议

    把这三个协议放到一起学习是因为这三个协议处于同一层,ARP协议用来找到目标主机的Ethernet网卡Mac地址,IP则承载要发送的消息.数据链路层可以从ARP得到数据的传送信息,而从IP得到要传输的数 ...

  6. 认识solr结构,了解核心的文件目录

    下载solr并解压后,发现solr的目录里有很多的东西,此时我们可能会感到很恐慌,不知如何下手,下面让我带你认识它. 1.解压后的solr目录结构如下: 虽然里面有很多的文件,但是我们需要的其实就两个 ...

  7. Atomikos 中文说明文档【转】

    Atomikos 翻译文档(英文文档来源:下载安装包中START_HERE.html)                                  ----译者:周枫 请尊重劳动成果,转载请标明 ...

  8. HDU1402 A * B Problem Plus FFT

    分析:网上别家的代码都分析的很好,我只是给我自己贴个代码,我是kuangbin的搬运工 一点想法:其实FFT就是快速求卷积罢了,当小数据的时候我们完全可以用母函数来做,比如那种硬币问题 FFT只是用来 ...

  9. IOS AVAUDIOPLAYER 播放器使用

    1. 导入 AVFoundation.framework 2.导入头文件  #import <AVFoundation/AVFoundation.h> 3. player = [[AVAu ...

  10. Fitnesse+RestFixture:Web服务回归测试利器

    RestFixture是Fitness的一个测试REST服务的插件,用于调用标准的http GET/POST等请求方法,并可以用XPath语法和Javascript语法检验http响应.本文介绍安装运 ...