标题## 用栈计算中缀表达式
中缀表达式也就是我们常说的普通数学表达式,那么怎么用栈来计算呢?请听我一一分解。
我们基本的思路是用两个栈来分别存储表达式中的数字和运算符,在通过有序的出栈来进行计算。(我用OPTR代表运算符栈,OPND代表运算数字栈)
方便判断表达式的结束,我会在运算符栈第一个元素和表达式后面加一个#
在此之前,先要把基础的功能函数写出来
1.判断是数字还是运算符
//判断计算表达式中下一个字符是数字还是运算符 ,返回0是代表运算数字,返回1代表运算符号
int in(char ch)
{
switch(ch)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return 1;
break;
default:
return 0;
}
}
2.比较运算符优先级的函数(这个是最关键的)
在给出代码之前,我先给一张对比表给大家看看
图片中的θ2表示运算符栈的栈顶元素,θ1表示读取到的运算符
大于号表示θ2的优先级高于θ1,小于就相反,等于就优先级相等
(下面代码我就用1,0,-1依次代表大于,等于,小于)
int precede(char topch,char ch)
{
if(topch=='+'||topch=='-')
{
if(ch=='+'||ch=='-'||ch=='#'||ch==')')
{
return 1;
}
else
{
return -1;
}
}
else if(topch=='*'||topch=='/')
{
if(ch=='(')
{
return -1;
}
else
{
return 1;
}
}
else if(topch=='(')
{
if(ch==')')
{
return 0;
}
else
{
return -1;
}
}
else if(topch=='#')
{
if(ch=='#')
{
return 0;
}
else
{
return -1;
}
}
else
{
return 2;
}
}
3.计算的函数
int operate(int num1,int num2,char operators)
{
int result;
switch(operators)
{
case '+': result=num1+num2; break;
case '-': result=num1-num2; break;
case '*': result=num1*num2; break;
case '/': result=num1/num2; break;
}
return result;
}
上面的基本函数给出来了,我们就来分析里面的具体逻辑吧
因为对数字的操作较简单,我先解决它:
(因为得到的都是字符型,所以需要减去一个’\0’来变成整形)
如果数字仅仅是一位数,那么就直接入栈就行了。
如果是两位以及更高位那么就需要把他拼接起来,之后就直接入栈,所对应的代码:
if(in(ch))
{
sum=ch-'0';
printf("******%d******\n",sum);
ch=getchar();
while(in(ch))
{
sum=sum*10+(ch-'0');
printf("******%d******\n",sum);
ch=getchar();
}
OPND=stack1push(OPND,sum);
}
数字解决完了,下面我们来解决运算符:
当读到的时字符时,我们先取运算符栈里的元素用来和读到的字符作比较
(1)如果返回的是-1(就代表栈顶元素优先级低于读取到的字符),我们就把读取到的字符入栈并且再执行一次 ch=getchar()
case -1:
OPTR=stackpush(OPTR,ch);
ch=getchar();
break;
(2)如果返回的是0(就代表栈顶元素优先级等于读取到的字符),我们就把运算符栈的栈顶元素出栈并且再执行一次 ch=getchar()
case 0:
x=stackpop(OPTR);
ch=getchar();
break;
(3)如果返回的是1(就代表栈顶元素优先级高于读取到的字符),我们就做运算:
运算符栈出一次栈,运算数字就出两次栈(两次出栈顺序要相反,因为出栈顺序是和入栈顺序相反的),把得到的结果压入运算数字栈内
并且不需要做ch=getchar()(下面我会解释)
case 1:
theta=stackpop(OPTR);
b=stack1pop(OPND);
a=stack1pop(OPND);
OPND=stack1push(OPND,operate(a,b,theta));
printfstack1(OPND);
break;
为什么返回1是要做ch=getchar(),而返回-1和0时不需要?
答:当返回1时有一种情况时读取到的是 ‘)’ ,此时如果还读取一次,运算符栈内的 ‘(’ 就不会出栈,后面的一系列运算就会出问题。当不是这种情况时,不读取也能够正常运算
所以不需要读取!!!
到此运算数字和运算符都搞定了,下面就是一些小问题(虽说是小问题,但一不小心就会很麻烦,本人亲测有效。。。)
(1)关于getchar的问题,先看一张图片
getchar 是从键盘上来读取我们的数据,但它们不是直接从键盘上来读取我们的数据。它们和键盘之间有一个区域叫缓冲区。
输入函数先来看缓冲区中是否有数据,如果有,它直接就拿走了,不需要从键盘输入,如果缓冲区什么都没有,则需要从键盘输入,再拿走。
比如说我们前面输入了一些需要的数字再来输入我们的表达式,在缓冲区就会有回车字符,当遇到getchar时,就会被读取,就导致读不到我们需要它读的字符。所以我们在此之前要先把缓冲区里清空在来进行我们的表达式输入
//把缓冲区中的内容全读走
while ( getchar() != '\n')
{
;
}
写上以上一段代码,妈妈再也不用担心getchar的问题了
(2)关于循环该采用怎样的型式以及循环里面的条件的问题
(这个问题一不小心就会导致全盘皆崩,请千万要小心处理)
do
{
}while(ch!='#'||getpop(OPTR)!='#');
这里我采用的时do–while循环,为什么呢?我来解释一下:
不论是 ch!=‘#’ 还是 getpop(OPTR)!=‘#’ ,都有另一个还没有进行完的情况就是:
(1)当 ch!=‘#’ 时,就代表已经读完了表达式,而此时运算符栈里面可能还有运算符没有做完,就还需要做进行一次运算
(2)当getpop(OPTR)!=‘#’ ,可能表达式还没读完,这种情况就比较简单了,就不解释了
到此,小毛病都解决的差不多了。下面我把完整的代码给出来
#include<string.h>
#include"function.cpp"
#include"charStack.cpp"
#include"intStack.cpp"
int main()
{
//printf("输入一个表达式(以#结尾):");
//定义一个操作符栈
charstack *OPTR;
OPTR=stackinit(OPTR);
OPTR=stackpush(OPTR,'#');
//定义一个操作数栈
intstack *OPND;
OPND=stack1init(OPND);
//定义一些相关的变量
char topch,ch,x,theta;
int sum,a,b,result;
while(ch=getchar()!='\n')
{
;
}
printf("输入一个表达式(以#结尾):");
ch=getchar();
//开始循环
do
{
if(in(ch))
{
sum=ch-'0';
printf("******%d******\n",sum);
ch=getchar();
while(in(ch))
{
sum=sum*10+(ch-'0');
printf("******%d******\n",sum);
ch=getchar();
}
OPND=stack1push(OPND,sum);
printfstack1(OPND);
}
else
{
topch=getpop(OPTR);
switch(precede(topch,ch))
{
case -1:
OPTR=stackpush(OPTR,ch);
printfstack(OPTR);
ch=getchar();
break;
case 0:
x=stackpop(OPTR);
printfstack(OPTR);
ch=getchar();
break;
case 1:
theta=stackpop(OPTR);
b=stack1pop(OPND);
a=stack1pop(OPND);
OPND=stack1push(OPND,operate(a,b,theta));
printfstack1(OPND);
break;
}
}
}while(ch!='#'||getpop(OPTR)!='#');
result=stack1pop(OPND);
printf("表达式的结果为:%d",result);
return 0;
}
其中function.cpp包含的是上面说过的基本函数
charStack.cpp和intStack.cpp 包含的是栈了一些基本运算,这里我就不过多介绍了,因为每个人都有自己的独特定义方式
最后,在此声明,本人写这个是把自己在编码过程中遇到的问题做一个总结,希望也能帮助到遇到困难的你。
如果此文章有什么不恰当的地方,希望大家在评论区留言,让我们一起来讨论一下。