后缀转中缀表达式(c实现)
我们同样是用栈来实现的,但其实现思路是有所不同的,有点类似于计算后缀表达式。
- 如果是数,就将它压入栈中
- 如果是运算符,就弹出对应数目的操作数,然而在这里我们并不计算该表达式,而是将它整个压入到栈中
- 我们都知道后缀表达式不存在优先级问题,但中缀表达式是需要考虑优先级的,因此,在转换过程中,我们需要在恰当的地方加上括号来保证运算过程的正确性
- 对于如何加括号的问题,我是这么想的:例如中缀表达式 a * (b + c) 转换为后缀是 a b c + * ,当我们将 a b c + * 转换回去的时候就需要在 b + c 外面加上括号。如果当前运算符的优先级比上一个运算符的优先级要高,我们就给栈顶的两个子表达式加上括号。
- 最后我们弹出栈顶表达式即可
思路大概就是这样,下面我会结合代码详细说明一下
同样,我们在主函数中控制整个转换过程,这个转换过程相比于上一个来说应该简单了很多
#define MAXEXP 1000 /* 输入的表达式的最大长度 */
#define MAXOP 100 /* 运算符或操作数的最大长度 */
#define NUMBER '0' /* 标识找到一个数 */
int main(int argc, const char *argv[])
{
int c;
int lastc;
char s[MAXEXP];
char *op2;
lastc = 0;
while ((c = getop(s)) != EOF)
if (c == NUMBER)
push(s);
else if (op_prior(c)) {
op2 = top();
pop();
cat_exp(top(), s, op2, c, lastc);
push(s);
lastc = c;
} else if (c == '\n') {
printf("%s\n", top());
pop();
}
}
getop函数主要用于解析输入参数,将它们分解为运算符或操作数,为了后续的处理,我们也做了一些额外的工作,我们用空格将每个运算符或操作数分隔开。当然在处理输入时,它会丢弃多余的空白符,但如果你不加分隔符的话,它会帮你加上的。
op_prior函数很简单,我们在中缀转后缀表达式的过程中用到过它,这里和那里的是相同的
int getop(char *s)
{
int c, i;
while ((s[0] = c = getchar()) == ' ' || c == '\t')
;
if (!isdigit(c) && c != '.') {
s[1] = ' ';
s[2] = '\0';
return c;
}
i = 0;
if (isdigit(c))
while (isdigit(s[++i] = c = getchar()))
;
if (c == '.')
while (isdigit(s[++i] = c = getchar()))
;
s[i] = ' ';
s[++i] = '\0';
if (c != EOF)
ungetc(c, stdin);
return NUMBER;
}
int op_prior(int c)
{
switch(c) {
case '*':
case '/':
case '%':
return 2;
break;
case '+':
case '-':
return 1;
break;
default:
return 0;
break;
}
}
下面的代码应该是这个程序的难点所在了
cat_exp函数用当前读入的运算符把栈顶的两个子表达式连接起来,如同上面提到的,这里需要重点考虑的是加括号的问题,代码中注释的很详细,应当都能看得懂
void cat_exp(char *op1, char *s, char *op2, int c, int lastc)
{
char tmp[MAXOP];
char op[MAXOP];
char opt[MAXOP];
strcpy(tmp, s);
if (lastc && op_prior(c) > op_prior(lastc)) {
if (isop(op1)) {
cat(op, op1);
op1 = op;
}
if (isop(op2)) {
cat(opt, op2);
op2 = opt;
}
}
strcpy(s, op1);
strcat(s, tmp);
strcat(s, op2);
pop();
}
void cat(char *s, char *op)
{
*s++ = '(';
while (*op != '\0')
*s++ = *op++;
while (*s != ' ')
s--;
*s++ = ')';
*s++ = ' ';
*s = '\0';
}
int isop(char *s)
{
while (*s != '\0')
if (op_prior(*s++))
return 1;
return 0;
}
最后就是栈的实现了,和中缀转后缀不同的是,这里是用指针栈来实现转换过程的,其实也就一普通的堆栈,没什么特别的。但其中也有几点是需要注意的:
- 我们需要实时为压入栈的表达式分配存储空间
- 不要忘记检查内存是否分配成功
- 出栈时记得释放内存
为了方便起见,我这里用断言以防止使用NULL指针,即内存分配失败的情况
#define MAXVAL 100
static char *val[MAXVAL];
static int sp = -1;
void push(char *s)
{
if (!isfull())
assert((val[++sp] = strdup(s)));
else
printf("stack is full\n");
}
void pop(void)
{
if (!isempty()) {
free(val[sp]);
val[sp--] = NULL;
} else
printf("stack is empty\n");
}
char *top(void)
{
if (!isempty())
return val[sp];
else {
printf("stack is empty\n");
return 0;
}
}
int isfull(void)
{
return sp == MAXVAL - 1;
}
int isempty(void)
{
return sp == -1;
}