在前面我们已经学会了给定一个计算式(这是如何把计算式转化为后缀式的思路)自己写出它的后缀表达式,接下来我们得尝试让C语言写出其转换方法
大概思路:
-
首先我们只进行只有加减的转换,下面且看例子
首先我们得明确,转换成的后缀式的数字和符号出现的顺序都没有改变,那么如果当计算式只含加减的符号时是不是可以这样想:把左边的计算式存入数组里,然后用指针对其一一识别,出现数字直接对其输出,而遇到运算符得等一会,等到遇到下一个运算符时直接输出,最后再把最后一个运算符进行输出
这时就有人会问了:为啥要等到下一个运算符呢
因为这里是求后缀表达式,对于一个单独的5+7而言,+要得在5和7的后面,而在面对5+7-8时,这个减号的位置就是转化为后缀式的加号的位置,因此下面的一个减号是定位前面的一个加号的,故等遇到-时加号再进行输出(最后一个符号直接再等都输出完了再输出即可)
-
现在我们加大一点点难度,进行加减乘除的运算
这个我们可能又开始不会了但如果改成:5-A+6-9不知大家还会不会呢
当然,在这里我们得明确其后缀式结果的特征,很明显这里此时*在-的前面,而除却*以外的加减符号则按照顺序排列,而*的排列规矩又是什么样的呢,我们得继续探究
如果按照上一道题的逻辑,前面的5+6输出很正常(+遇到了56后面的-然后直接输出,然后得把-存入栈中),但是-遇到的下一个符号是*,而结果是-在*的后面,仔细一想后缀表达式的*输出的位置同计算式8*7后面的+的位置,因为(5+6)(8*7)-这里可以把括号里面的看作一个元素,那么我们只要在*遇到栈中的-时不进行输出,而把*存起来,当后面的+遇到*时再把*输出,此时再把前面一个-进行输出,而这种做出这种思路的方法,就是算数优先级
+- | */ | () |
---|---|---|
1 | 2 | 0 |
先忽略()为什么是0
以上面的5+6-8*7+9为例,我们先把+存入栈中,当下面一个优先级相等的-遇到+时,直接输出+,再把-存入栈中,下一个优先级大的*遇到减号时,存入栈中,当再下一个-遇到*时优先级比*小,*输出,然后接着和上面一个-比,优先级相等,上面一个-输出,然后最后的-进行输出(生动一点的就是 *优先级比-大因此得比-先计算,即先输出,而输出的位置又是下一个+的位置)
-
接下来就是()
首先我们得清楚为啥()的优先级要比+-/*都小,我们要清楚,符号的出栈规则为上面一个符号的优先级大于等于遇到的这一个时(谁大谁先输出,也就是后入栈),才输出,而()不能进行输出,故优先级为0
接下来是几个例子
(5+8)-7 | 58+7- |
---|---|
7-(5+8) | 758+- |
4-(5-7*8)+9 | 4578*--9+ |
这里可能还是一脸懵逼,但是如果我把结果也加个括号就变成了
这里就有人会说了:这个结果不能带括号,我这里呢是想告诉各位:括号里面的运算逻辑其实呢和原本的运算逻辑是一样的(咳咳,我有那意思,就是不咋会表达,如果各位没理解的话还是直接看下面的事例分析吧)
以4-(5-7*8)+9为例
4输出,-输入,当判断是(时不能和前面的-进行比较,必须得是()外面的+9中的+与()前面的-进行比较(括号里的你就把它当成一个元素),那么当是(时直接入栈,接着是5输出,-优先级比(大,入栈,7输出,*优先级比-大,入栈,8输出,然后是),遇到是)时又不断出栈,即把*和-进行输出,然后再把(进行出栈
这里可能还是有人不太了解,我再举个简单的例子
7-(5+8)这里的7输出,-入栈,(入栈,+入栈,)时回退到前面找(,然后+出栈,(直接出栈但不输出
7-(5+8-6)这里的5+8-6时的-遇到前面入栈的+时+出栈
下面是入栈的一些函数
#define ERROR 0
#define OK 1
#define STACK_INT_SIZE 10
#define STACKINCREMENT 5
typedef char ElemType;
typedef struct
{
ElemType* base;
ElemType* top;
int stacksize;
} SqStack;
int initStack(SqStack* s);
int emptyStack(SqStack* s);
int pushStack(SqStack* s, ElemType e);
int popStack(SqStack* s, ElemType* e);
int getTop(SqStack* s, ElemType* e);
int initStack(SqStack* s) //栈的创建
{
s->base = (ElemType*)malloc(STACK_INT_SIZE * sizeof(ElemType));
if (!s->base)
return ERROR;
s->top = s->base;
s->stacksize = STACK_INT_SIZE;
return OK;
}
int emptyStack(SqStack* s) //判断是否为栈空
{
return s->base == s->top;
}
int pushStack(SqStack* s, ElemType e) //入栈
{
if (s->top - s->base >= s->stacksize)
{
s->base = (ElemType*)realloc(s->base, (s->stacksize + STACKINCREMENT) * sizeof(ElemType));
if (!s->base)
return ERROR;
s->top = s->base + s->stacksize;
s->stacksize += STACKINCREMENT;
}
*s->top++ = e;
return OK;
}
int popStack(SqStack* s, ElemType* e) //出栈
{
if (emptyStack(s))
return ERROR;
s->top--;
*e = *s->top;
return OK;
}
int getTop(SqStack* s, ElemType* e) //取栈顶元素
{
if (emptyStack(s))
return ERROR;
*e = *(s->top-1);
return OK;
}
这里关于栈的相关知识以后有时间再一一总结,故在此跳过,为了便于大家理解,下面直接先介绍主函数
int main() { char result[80] = { '\0' }, exp[80] = { '\0' }; //补充代码实现读入多个表达式,并输出每个表达式的后缀式 while (1) { scanf("%s", exp); if (exp[0] == '#') { break; } convert(exp, result); printf("%s\n", result); } return 0; }
-
以单独输出#结束,故有esp[0]=='#'
-
输入的数存入exp里面,输出的数存入result里面,而convert则是对exp的数进行处理
这一个是对算数优先级处理的函数
int f(char c)
{
switch (c)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
这个则是用于后缀表达式的函数
void convert(char* exp, char* result) {
SqStack s;
initStack(&s);
char* p = exp;//p指向exp
char* r = result;//r指向result
while (*p != '#')
{
if (isdigit(*p))//判断*p是否为数字
{
while (isdigit(*p))
*r++ = *p++;
*r++ = ' ';//保证这个数后面有空格
}
else if (*p == '(')//当*p为(时直接存入栈中
pushStack(&s, *p++);
else if (*p == ')')
{
ElemType e;
while (getTop(&s, &e) && e != '(')
{
popStack(&s, &e);//这里是对()中残余的符号进行输出
*r++ = e;
*r++ = ' ';
}
popStack(&s, &e); //这里是把(进行出栈
p++; //p指向下一个位置
}
else if (f(*p) > f(*(s.top - 1)))//优先级大于栈顶的入栈
pushStack(&s, *p++);
else
{
while (!emptyStack(&s) && f(*p) <= f(*(s.top - 1)))
{
popStack(&s, r++);
*r++ = ' ';
}//优先级小于栈顶的一直出栈
pushStack(&s, *p++);
}
}
while (!emptyStack(&s))//把残余的出栈
{
popStack(&s, r++);
*r++ = ' ';
}
*r = '\0';
}