栈的应用1——超级计算器(中缀与后缀表达式)C语言
2015-08-22 12:15
295 查看
这里要学的程序主要用来实现一个功能——输入表达式输出结果,也就是一个计算器。效果如下:
![](http://images0.cnblogs.com/blog2015/801450/201508/241842293112236.png)
这个程序主要有两个步骤:1、把中缀表达式转换为后缀表达式;2、计算后缀表达式的结果。
首先先明白几个问题:
1、为什么要转换为后缀表达式?因为后缀表达式容易实现计算机计算结果。(可以百度一下后缀表达式,又称逆波兰式)
2、怎么把中缀表达式转换为后缀表达式?
3、怎么用后缀表达式输出结果?
相信如果弄明白了上面几个问题,有C语言基础的同学就可以编出这个程序啦。而后面两个问题都要用到一个数据结构——栈。实际上数据结构只是一种思想,一种思维,是连接人脑与计算机的桥梁(纯属个人杜撰= =)
好,那么我们先学怎么把中缀表达式转换为后缀表达式。
为了简化问题,我们不妨设输入的数字都是一位整数,这样读入的时候只需要以字符的方式逐个读入即可,也就是说读入的必定是数字或者+-*/,读到回车视为结束;另外不妨直接将结果打印到屏幕上,也就是说结果不必存储(这样我们可以更多地关注算法本身,而非输入输出方式的细枝末节)
类似上面的图片,它的后缀表达式是 9 3 1 - 2 * + 5 2 / + 怎么得到的呢?下面开始讲这个转换的算法:
1、设置一个栈,栈底填一个"@"作为结束符(当然也可以用别的符号,至于为什么要用一会讲优先级的时候会说);
2、开始逐个字符读入;
3、如果读入的是数字,直接打印printf("%c ",c);(数字后面打印一个空格作为间隔符,要不然没法看了931-。。。不解释)
4、如果读入的是"("直接进栈;
5、如果读入的是")",说明前面肯定读入过一个"("找到这个左括号,把两者之间的符号逐个弹栈(这里要说明的是,括号不必打印,因为后缀表达式没有括号);
6、如果不是上面的几种情况,那必定是+-*/中的一个啦,这样来说就容易多了。本来这里应该说优先级的问题,可是我还是想先说说栈。讲到这里,实际上大家应该也看出来了,栈实际上就是一个进栈和出栈的问题,这里也是一样,对于表达式这里我们可以发现,这个栈是用来存储符号的,()+-*/,所以我们只需要明白什么时候符号可以进栈,什么时候符号可以出栈就可以啦~上面已经讲了两个了,左括号的时候直接可以进栈,右括号的时候把两者之间的出栈打印。而对于+-*/,只需要记住一个法则,对于读入的这个符号,只有它比栈顶符号的优先级高的时候才可以进栈(优先级相同也不能进栈),而它不能进栈,就只能让栈顶的出栈啦~所以不断出栈,知道这个符号可以进栈,这个新读入的符号就算处理完成啦。(优先级函数可以参照后面的程序代码)
OK,按照上面的算法,扫描完一遍读入的中缀表达式,就可以在屏幕上输出后缀表达式啦。下面附上自制代码(C语言,带注释):
好,如果你看懂了上面所有的内容,恭喜你,已经学会一半了。那么,现在我们就可以去掉那个只能输入一位数的大前提,只需要在输入的时候处理一下,就可以实现任意位数的数,甚至是小数(可以参照最后的程序代码)。
那么现在我们来完成第二步:已知后缀表达式输出结果。这个算法要比上面的转换简单多了,就是栈的基本操作,只不过这里的栈不是用来存储字符的,而是用来存储数字的。
我们不妨还是假设一位数吧。
1、如果读入的是数字,直接进栈;
2、如果是符号,必然是+-*/中的一个,直需要弹出栈顶的两个数,运算,然后再把结果进栈。直至扫描完整个后缀表达式,栈顶就是最终结果。
如果看懂了上面,我们可以发现,这个过程直接就可以在转换表达式的时候顺便完成,也就是,如果遇到数字,不打印到屏幕上,而是进栈到数字存储栈里;如果有出栈的符号,不用打印到屏幕上,而是弹出数字栈的两个栈顶元素,然后进栈。就OK啦。下面附上输入中缀表达式输出结果的代码(只是将上面的代码中打印的过程换成了其他操作而已):
OK,会了一位数的,其他的也就是小case啦,如果想看完整的多位数的代码,可以参看 栈的应用2——超级计算器(中缀与后缀表达式)C语言
![](http://images0.cnblogs.com/blog2015/801450/201508/241842293112236.png)
这个程序主要有两个步骤: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 0; else if (x=='*'||x=='/') return 1; else if (x=='@'||x=='('||x==')') return -1; } int main() { stype *s,*top; //栈指针和栈顶指针 char c; s=newp; s->dat='@'; s->next=NULL; top=s; c=getchar(); //此后为读取中缀表达式的部分,用字符一个一个的读,直到读到回车 while(c!='\n') { if (c>='0'&&c<='9') //如果读入数字,直接打印 { 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 0; //后缀表达式输出完毕 }
好,如果你看懂了上面所有的内容,恭喜你,已经学会一半了。那么,现在我们就可以去掉那个只能输入一位数的大前提,只需要在输入的时候处理一下,就可以实现任意位数的数,甚至是小数(可以参照最后的程序代码)。
那么现在我们来完成第二步:已知后缀表达式输出结果。这个算法要比上面的转换简单多了,就是栈的基本操作,只不过这里的栈不是用来存储字符的,而是用来存储数字的。
我们不妨还是假设一位数吧。
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 0; else if (x=='*'||x=='/') return 1; else if (x=='@'||x=='('||x==')') return -1; } int main() { int rs=0; stype *s,*top; //栈指针和栈顶指针 int calc[50],i=0; char c; s=newp; s->dat='@'; s->next=NULL; top=s; c=getchar(); //此后为读取中缀表达式的部分,用字符一个一个的读,直到读到回车 while(c!='\n') { if (c>='0'&&c<='9') //如果读入数字,直接打印 { i++; calc[i]=c-48; } 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-1]=calc[i-1]+calc[i]; i--; } else if (top->dat=='-'){ calc[i-1]=calc[i-1]-calc[i]; i--; } else if (top->dat=='*'){ calc[i-1]=calc[i-1]*calc[i]; i--; } else if (top->dat=='/'){ calc[i-1]=calc[i-1]/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-1]=calc[i-1]+calc[i]; i--; } else if (top->dat=='-'){ calc[i-1]=calc[i-1]-calc[i]; i--; } else if (top->dat=='*'){ calc[i-1]=calc[i-1]*calc[i]; i--; } else if (top->dat=='/'){ calc[i-1]=calc[i-1]/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-1]=calc[i-1]+calc[i]; i--; } else if (top->dat=='-'){ calc[i-1]=calc[i-1]-calc[i]; i--; } else if (top->dat=='*'){ calc[i-1]=calc[i-1]*calc[i]; i--; } else if (top->dat=='/'){ calc[i-1]=calc[i-1]/calc[i]; i--; } top=top->next; free(s); } printf("%d\n",calc[1]); return 0; }
OK,会了一位数的,其他的也就是小case啦,如果想看完整的多位数的代码,可以参看 栈的应用2——超级计算器(中缀与后缀表达式)C语言
相关文章推荐
- C++ 编程个人总结
- C++中 explicit的用法
- GCC在C语言中内嵌汇编 asm __volatile__ (2)
- C语言编程技巧----如何定义全局变量----->提高代码可移植性
- C和C++中的文件读入写出区别
- C++联合
- Effective C++ 条款8 别让异常逃离析构函数
- 为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈
- C++模板的理解与使用
- C++模板的理解与使用
- 阶乘、斐波那契、 1的个数统计 c语言 实现备忘
- C++内存分配
- 用vs2013编写C语言与编写C++的区别
- C语言 malloc calloc realloc alloc 在分配内存时的 区别
- Majority Element
- C语言的二维和三维动态连续数组分配
- C语言中移位符需要注意的地方
- C++基本知识归纳(2)
- 浅谈C++中指针和引用的区别
- c++的赋值构造函数以及vector::push_back的问题