后缀转中缀表达式(c实现)

后缀转中缀表达式(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函数很简单,我们在中缀转后缀表达式的过程中用到过它,这里和那里的是相同的
/*  getop函数:从输入中获取下一个运算符或操作数  */
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;
}

/*  op_prior函数:返回对应运算符的优先级  */
int op_prior(int c)
{
    switch(c) {
    case '*':
    case '/':
    case '%':
        return 2;
        break;
    case '+':
    case '-':
        return 1;
        break;
    default:
        return 0;
        break;
    }
}
下面的代码应该是这个程序的难点所在了
cat_exp函数用当前读入的运算符把栈顶的两个子表达式连接起来,如同上面提到的,这里需要重点考虑的是加括号的问题,代码中注释的很详细,应当都能看得懂
/*  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);     /*  先将已经生成的中缀表达式拷贝到tmp中  */
    if (lastc && op_prior(c) > op_prior(lastc)) {
        if (isop(op1)) {
            cat(op, op1);
            op1 = op;
        }        /*  在可能需要加括号的地方都加上括号  */
        if (isop(op2)) {
            cat(opt, op2);
            op2 = opt;
        }
    }     /*  将新连接好的子表达式加入到已经生成的表达式中,然后拷贝回s中  */
    strcpy(s, op1);
    strcat(s, tmp);
    strcat(s, op2);
    pop();
}

/*  cat函数:在表达式前后加上括号  */
void cat(char *s, char *op)
{
    *s++ = '(';
    while (*op != '\0')
        *s++ = *op++;
    while (*s != ' ')
        s--;
    *s++ = ')';
    *s++ = ' ';
    *s = '\0';
}

/*  isop函数:判断s中是否有运算符  */
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;

/*  push函数:将字符串s压入栈中  */
void push(char *s)
{
    if (!isfull()) 
        assert((val[++sp] = strdup(s)));
    else           /*  将s保存在某个安全的地方  */
        printf("stack is full\n");
}

/*  pop函数:弹出栈顶值  */
void pop(void)
{
    if (!isempty()) {
        free(val[sp]);
        val[sp--] = NULL;
    } else
        printf("stack is empty\n");
}

/*  top函数:返回栈顶值  */
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;
}

猜你喜欢

转载自blog.csdn.net/qq_41145192/article/details/80548801