前言:
中缀表达式是我们人类惯用的一种计算表达式。如:1+2+3,计算数在两边,符号在中间故称之为中缀表达式。但中缀表达式有其局限性,计算必须按照符号的优先级来进行计算。如果不按照优先级来计算必须加上括号来计算。比如2+2*3,必须先算出2*3再加上2。如果要先算2+2再乘以3必须加上括号(2+2)*3.这样的操作对于计算机来说太过于复杂,而且如果计算机真的要按这种方式来运行的话算法的复杂度会很高。因此后缀表达式横空出世了,用后缀表达式可以简化很多操作,而且算法的复杂度可以减少到O(n)的效果。以上面(2+2)*3为例,转换为对应后缀表达式是2 2+ 3 *。这样就可以不用考虑括号,和符号优先级的问题了。很适合计算机的线性运行。
算法书上讲解中缀表达式到后缀表达式的转换,几句话就搞定了。因此需要看很长的时间才能完全的理解。这样做对写代码是非常不好的,由简单到复杂对于写代码是有利的,因为写代码还有一个调试的步骤要走,所以一开始把复杂的实现会难以调试。因此本文不打算这么干,本文分三步进行讲解.1.一个错误的中缀表达式到后缀表达式的转换。2.正确的转换。3.括号的处理.
事前准备
首先,为了简化操作这里不考虑表达式是否合法。用一个普通的表存放已经转换好的表达式(下文称:已转换表)。用一个栈存放临时的符号.符号有优先级之分所以先需要定义好每个符号属于哪一个级别的。这里只定义两级,(加减)和(乘除)。
1.一个错误的转换
首先这里不考虑减法和除法不满足交换率的定理。只保证一个大致方向上转换正确的后缀表达式。
测试的中缀表达式如下。
a+b-c-d*a+b*a
转换后的结果为:
abcda*ba*+--+
算法的中心思想是:
首先第一个符号进栈,当遍历到后面的符号时。弹出栈中符号并比较两个符号的优先级,低的进栈,高的放到转换表。
c= symstack[--symidx]; //pop a symbol
if(getindex(*pt)>getindex(c)){ //getindex 取得符号的优先级
*pm++ = *pt; //pm 是指向已转换表的指针
++symidx; //由于弹出来的符号优先级比遍历的要小,所以还原栈
}else{
*pm++= c; //弹出来的符号优先级比遍历的大,放入到转换表
symstack[symidx++]=*pt;
}
(代码说明:symstack是存放符号的临时栈,symidx是栈的下标)。
2.一个正确的转换
上面的转换会有问题的,减法和除法是不满足交换率的。所以这样转换看似正确了。但不是我们要的结果。
正确的转换应该是 a-b-c ==> ab-c- 而不是abc--.
所以经过修改后算法的中心思想是:
栈底存的永远是优先级最低的一个,栈顶的符号优先级最高。每当遇到一个符号时,将它与栈中符号(优先级)从高到低进行比较,高的弹出,低的留下。若栈全部弹出,则留下优先级最低的一个符号,直到最后才放到已转换表中。(ps:若遍历的符号与栈中的符号优先级相同则按他们各自出现的顺序放入到栈中。)
代码修改如下。
while(true)
{
c=symstack[--symidx];
if(getindex(*pt)>getindex(c)){//如果外面的符号比栈里
++symidx; //面的符号优先级高
symstack[symidx++]=*pt; //直接按优先级顺序把比较
break; //的两个符号放入栈中。
}else{
*pm++= c; //放入到已转换表中去
if(symidx==0){//如果栈空,则留下优先级最低的一个并弹出
symstack[symidx++] = *pt;
break;
}//thancontinue
}
}
3.处理括号
这里没有把括号做为符号来进行处理,而是把括号做为表达式中的表达式来处理。如果遍历时遇到左括号'(',直接入栈。直到碰到右括号')‘才将左括号弹出。仔细想想这其实是一种递归思想。表达式里还有一层表达式,先处理内层的表达式。再处理外层的表达式。
只要将上面的代码做一点修改即可完成,以下是整个函数的代码。
void in2sufix(char *mid) //First, I resume this experssion that it has no wrong
{
char symstack[BUFSIZE];
memset(symstack,'0',BUFSIZE);
int symidx=0;
const int LEN = strlen(mid)+1;
char *temp =(char*)malloc(LEN+1); //temp memory
strcpy(temp,mid); //copy experssion to temp memory
memset(mid,'\0',LEN);
char *pt=temp;
char *pm=mid;
char c;
while(*pt != '\0')
{
if(issym(*pt)) //process symbol
{
if(0==symidx) //the stack is empty
symstack[symidx++]=*pt;
else{
if(*pt == ')')
{//pop all symbol in parentheses
while(symstack[--symidx] != '(')
*pm++ = symstack[symidx];
symstack[symidx] = '\0';
}
else if(*pt == '(')
symstack[symidx++] = *pt;
else{
while(true)
{
c=symstack[--symidx];
if(c=='(' || getindex(*pt)>getindex(c)){
++symidx;
symstack[symidx++]=*pt;
break;
}else{
*pm++ = c;
if(symidx==0){
symstack[symidx++] = *pt;
break;
}//than continue
}
}
}
}
}
else
*pm++ = *pt;
pt++;
}
while (0 <symidx) // pop the last symbol
*pm++ = symstack[--symidx];
*pm = '\0';
free((void*)temp);
temp = NULL;
}