《数据结构和算法》之中缀表达式、后缀表达式转换

一,在上篇博文中讨论了逆波兰表达式的计算问题,在这里讨论一下中缀表达式如何转换为后缀表达式

       问题示例:如何将1+(2-3)*4+10/5 转换为后缀表达式 1 2 3 - 4 * + 10 5 / +这样的输出

       问题分析:

       第一步,首先遇到第一个输入是数字1,数字在后缀表达式中都是直接输出,接着是符号“+”入栈

                                              

       第二步,第三个字符是“(”,依然是符号,这个时候将此符号入栈,接着是数字2,直接输出,然后是符号“-”,这个时候符号仍然入栈

                                                  

     第三步,接下来是数字3,输出,紧跟着是“)”,此时,我们需要匹配栈里的“(”,然后再匹配前将栈顶数据依次出栈:

                                           

     第四步,紧接着是符号“*”,直接入栈:

                                            

      第五步,遇到数字4,直接输出,之后是符号“+”,此时栈顶元素是符号“*”,按照先乘除后加减原理,此时栈顶的乘号优先级比即将入栈的加号要大,所以出栈。而栈中的第二个元素是加号,按照先到先后的原则,这个时候“+”也要出栈。

                                           

       第六步,紧接着是数字10,直接输出,最后是符号“/”,进栈:

                                          

      第七步,最后一个数字5,这个时候直接输出5,但是栈里仍然有数据,此时可以将栈中符号依次出栈。

二,代码分析:

                 

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define STACK_INIT_SIZE 20
#define STACKINCREMENT 10
#define MAXBUFFER   10

typedef char ElemType;
typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;

void InitStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if( !s->base )
        exit(0);

    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;

}

void Push(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)
             exit(0);

        s->top = s->base + s->stackSize;
        s->stackSize = s->stackSize + STACKINCREMENT;
    }

    *(s->top) = e;
    s->top++;

}

void Pop(sqStack *s, ElemType *e)
{
    if( s->top == s->base)
        return;

    *e = *--(s->top); //将栈顶元素弹出并修改栈顶指针

}

int StackLen(sqStack s)
{
    return (s.top - s.base);
}

int main()
{
    sqStack s;
    ElemType c,e;


    InitStack( &s );

    printf("请输入中缀表达式,以#为结束标志:");
    scanf("%c", &c);

    while( c != '#')
    {
        if( c >= '0' && c <= '9')
        {
            printf("%c", c);
        }
        else if( ')' == c )
        {
            Pop(&s, &e);
            while('(' != e)
            {
                printf("%c", e);
                Pop(&s, &e);
            }
        }
        else if( '+' == c || '-' == c)
        {
            if( !StackLen(s) )
            {
                Push(&s, c);
            }
            else
            {
                do
                    {
                      Pop(&s, &e);
                       if( '(' == e)
                       {
                           Push(&s, e);
                       }
                       else
                        {
                           printf("%c", e);
                       }
                    }while( StackLen(s) && '(' != e);
                    Push(&s, c);
            }
        }
        else if( '*' == c || '/' == c || '(' == c)
        {
            Push(&s, c);
        }
        else
        {
            printf("输入错误,请重新输入!\n");
            return -1;
        }
        scanf("%c", &c);
    }

    while( StackLen(s))
    {
        Pop( &s, &e);
        printf("%c", e);
    }
    return 0;
}

              最后的结果

                                  

           大家可以看到这样的结果直接连在一起了,很难分辨出来是123还是1 2 3,这个时候就要在输出做一个改动,将printf("%c"改为printf("%c ",加了一个空格,但是出现了这样的结果:

                                     

              这样可以看到10被拆分成了1 0,显然这样是不合理的。此时再修改程序,将主函数main修改如下:

                        

int main()
{
    sqStack s;
    ElemType c,e;


    InitStack( &s );

    printf("请输入中缀表达式,以#为结束标志:");
    scanf("%c", &c);

    while( c != '#')
    {
        while( c >= '0' && c <= '9')
        {
            printf("%c", c);
            scanf("%c",&c);
            if( c<'0' || c>'9')
            {
                printf(" ");
            }
        }

        if( ')' == c )
        {
            Pop(&s, &e);
            while('(' != e)
            {
                printf("%c ", e);
                Pop(&s, &e);
            }
        }
        else if( '+' == c || '-' == c)
        {
            if( !StackLen(s) )
            {
                Push(&s, c);
            }
            else
            {
                do
                    {
                      Pop(&s, &e);
                       if( '(' == e)
                       {
                           Push(&s, e);
                       }
                       else
                        {
                           printf("%c ", e);
                       }
                    }while( StackLen(s) && '(' != e);
                    Push(&s, c);
            }
        }
        else if( '*' == c || '/' == c || '(' == c)
        {
            Push(&s, c);
        }
        else if( '#' == c)
        {
            break;
        }
        else
        {
            printf("输入错误,请重新输入!\n");
            return -1;
        }
        scanf("%c", &c);
    }

    while( StackLen(s))
    {
        Pop( &s, &e);
        printf("%c ", e);
    }
    return 0;
}

             最终得出想要的满意结果:

                                  

                   


                              



                               


  • 32
    点赞
  • 134
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
### 回答1: 这个实验的目的是将一般算术表达式转换成后缀式。后缀式也叫逆波兰式,是一种不含括号的表达式,运算符在操作数的后面,因此也叫后缀表达式转换后的后缀式可以方便地进行计算,而且不需要考虑运算符的优先级和括号的问题。 转换算法的基本思路是利用栈来存储运算符,遇到操作数直接输出,遇到运算符则将其与栈顶运算符比较优先级,如果栈顶运算符优先级高,则将栈顶运算符弹出并输出,直到栈顶运算符优先级低于当前运算符或者栈为空,然后将当前运算符入栈。最后将栈中剩余的运算符依次弹出并输出。 例如,将中缀表达式"3+4*5-6"转换后缀表达式的过程如下: 1. 从左到右扫描中缀表达式,遇到操作数直接输出,即输出3; 2. 遇到运算符"+",将其入栈; 3. 遇到操作数4,直接输出,即输出4; 4. 遇到运算符"*",由于"*"的优先级高于"+",所以将其入栈; 5. 遇到操作数5,直接输出,即输出5; 6. 此时栈顶为"*",优先级高于"+",所以将"*"弹出并输出,即输出4 5 *; 7. 遇到运算符"-",由于"-"的优先级低于"*",所以将"-"入栈; 8. 遇到操作数6,直接输出,即输出6; 9. 此时栈顶为"-",优先级低于"*",所以将6入栈; 10. 扫描结束,将栈中剩余的运算符依次弹出并输出,即输出3 4 5 * + 6 -。 最终得到的后缀表达式为"3 4 5 * + 6 -"。 ### 回答2: 一般算术表达式是指包含加、减、乘、除等运算符及其优先级,以及括号的表达式。在计算机中,我们需要将这种表达式转换成后缀式,以方便计算机的处理。后缀式也叫逆波兰式,是一种把运算符后置的表达式,例如将中缀表达式“9+5*3-8/4”转换后缀表达式为“9 5 3 * + 8 4 / -”。 算法思想: 我们可以借助栈来实现中缀表达式后缀表达式,具体来说,我们需要先遍历一遍中缀表达式,然后按照优先级将操作符加入栈中,将数字直接输出。遍历完成之后,将栈中所有操作符按照顺序依次输出,这时候得到的序列就是后缀表达式。 具体实现: 1. 遍历中缀表达式,对于数字直接输出。 2. 对于遇到的操作符,如果是左括号,直接入栈;如果是右括号,则将栈中的操作符出栈,直到遇到左括号停止,并将左右括号丢弃;如果是其他操作符,则需要将栈中比当前操作符优先级更高的操作符全部出栈,放到后缀表达式中,然后将当前操作符入栈。 3. 遍历完成后,将栈中操作符全部出栈并添加到后缀表达式中。 4. 最后得到的序列就是后缀表达式。 代码实现: ```python def infixToPostfix(expression): operators = {'+': 1, '-': 1, '*': 2, '/': 2, '(': 0, ')': 0} # 操作符优先级 stack = [] postfix = [] for ch in expression: if ch.isdigit(): # 直接输出数字 postfix.append(ch) else: if ch == '(': stack.append(ch) elif ch == ')': while stack and stack[-1] != '(': postfix.append(stack.pop()) if stack and stack[-1] == '(': stack.pop() else: while stack and operators[ch] <= operators[stack[-1]]: postfix.append(stack.pop()) stack.append(ch) while stack: postfix.append(stack.pop()) return postfix ``` 总结: 通过栈的使用,我们可以很方便地将一般算术表达式转换成后缀式,并且复杂度为O(n),非常高效。实际应用中,后缀表达式可以直接计算,比中缀表达式更便于计算。因此,这种算法也有很广泛的应用场景。 ### 回答3: 在编程领域中,算术表达式的转换是一项非常重要的任务。而在数据结构实验中,我们学习到了如何将一般算术表达式转换成后缀式。下面,我将从以下几个方面进行阐述。 一、算法思路 将一般算术表达式转换成后缀式的算法思路是将符号按照操作优先级依次入栈和出栈,这个过程需要遵守以下几个规则: 1、如果是操作数,直接输出 2、如果是左括号,入栈 3、如果是右括号,弹出栈中所有运算符,并将它们输出,直到遇到左括号为止,但是左括号不输出 4、如果是运算符,弹出所有优先级大于或等于该运算符的栈顶运算符,然后将该运算符入栈 5、最后,将栈中的所有运算符依次弹出并输出,不包括左括号 二、具体实现 在具体实现上,我们需要创建两个栈,一个用于存储运算符,一个用于输出后缀式,然后遍历算术表达式,根据上述算法思路将符号依次入栈和出栈即可。具体代码实现如下: void transform(char exp[], char postfix[]) { char ch, elem; int i = 0, k = 0; Stack<char> s; while ((ch = exp[i++]) != '\0') { if (ch == '(') s.push(ch); else if (isdigit(ch) || isalpha(ch)) { postfix[k++] = ch; } else if (ch == ')') { while ((elem = s.pop()) != '(') { postfix[k++] = elem; } } else { while (!s.isEmpty() && priority(s.peek()) >= priority(ch)) { postfix[k++] = s.pop(); } s.push(ch); } } while (!s.isEmpty()) { postfix[k++] = s.pop(); } postfix[k] = '\0'; } int priority(char ch) { if (ch == '(') return 0; if (ch == '+' || ch == '-') return 1; if (ch == '*' || ch == '/') return 2; return 3; } 三、算法的应用 将一般算术表达式转换成后缀式在编程中有着广泛的应用,例如表达式求值、编译器中的语法分析等。 最后,要注意的是,在转换过程中,应当考虑数据类型及精度等问题,并严格按照算法思路进行实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值