"Data Structure": Infix Expression to Postfix Expression + Calculation of Postfix Expression

  • Added a code to judge the legality of the input infix expression:

"Data Structure": Judging the Legality of Infix Expressions - Amentos Blog - CSDN Blog

 

Table of contents

1. Basic concepts

2. Infix expression to postfix expression

   Example The infix expression 2*(3+5)+7/1-4 is converted to a postfix expression

3. Calculation of suffix expressions

   Example Calculation of postfix expression 2 3 5 + * 7 1 / + 4 -

4. Algorithm implementation

5. Algorithm improvement


1. Basic concepts

1. Infix expression:

        The operator is located in the middle of the operand in the form of infix (such as: 3+2), which is our common expression method of arithmetic and logic formulas in daily life.

2. Suffix expression:

        Also known as Reverse Polish Notation ( RPN ), the operator is placed after the two operands in suffix form (for example: the suffix expression of 3+2 is 3 2 +).

3. Prefix expression:

        Also known as Polish ( Polish Notation ), the operator is placed in front of the two operands in the form of a prefix (for example: the prefix expression of 3+2 is + 3 2).

Infix expressions often need to use parentheses to enclose operators and corresponding operands to indicate the order of operations

eg: 5*(2+1) Although the priority of * is higher than that of + , the existence of parentheses means that the + operation inside the parentheses should be executed first .  

Infix expressions are suitable for human thinking structure and computing habits, but not for computers

Especially infix expressions containing parentheses are very complex structures for computers.

Postfix expressions for computers

Unlike infix expressions, postfix expressions do not require parentheses to indicate operator precedence.

Calculations of postfix expressions are performed sequentially in the order in which operators  appear from left to right (regardless of the precedence between operators), which is a relatively simple structure for computers. 

2. Infix expression to postfix expression

Traverse each character of the infix expression from left to right (need to prepare a character stack to store operators and parentheses)

1. Characters are operands :  

Send directly to the postfix expression ( note: the complete operand needs to be analyzed first ).

2. The characters are left brackets :

Push it directly into the stack ( note: after the left parenthesis is pushed onto the stack, the priority drops to the lowest ).

3. The characters are closing brackets :

Pop the stack directly, and send the popped characters into the suffix expression one by one until the top character of the stack is a left parenthesis (the left parenthesis is also popped from the stack, but not sent to the suffix expression).

Summary: As long as the top of the stack is a left parenthesis, the stack can be popped for the last time.

4. The character is the operator :

If the stack is empty, it is pushed directly onto the stack.

If the stack is not empty, judge the operator on the top of the stack. If the priority of the operator on the top of the stack is lower than that of the operator, the operator will be pushed onto the stack; otherwise, the stack will always be popped, and the popped characters will be sent to the suffix expression in turn until the stack is empty. Or the operator at the top of the stack has a lower priority than this operator, and this operator is pushed onto the stack.

Summary: As long as the stack is empty or the priority is higher than the operator at the top of the stack , popping can be stopped and the operator can be pushed onto the stack.

5. Repeat the above steps until the traversal of the infix expression is completed, then judge whether the character stack is empty, if it is not empty, pop it directly, and send the popped characters into the suffix expression in turn.

Note: After the traversal of the infix expression is completed, there may still be characters in the stack that have not been output, so it is necessary to judge the stack to be empty.

   Example        infix expression 2*(3+5)+7/1-4 converted to postfix expression   

Traverse the characters of the infix expression in turn from left to right:

The first character is the operand , output directly:


The second character is an operator , which satisfies the condition that the stack is empty /priority is higher than the operator on the top of the stack , and the operator is pushed onto the stack: 


The third character is a left parenthesis , which is directly pushed onto the stack ( after the stack is pushed, the priority is reduced to the lowest ):


The fourth character is the operand , directly output:


The fifth character is an operator , which satisfies the condition that the stack is empty / priority is higher than the operator on the top of the stack  , and the operator is pushed onto the stack:


The sixth character is the operand , output directly:


The seventh character is a right parenthesis , which is directly popped out of the stack and output until the top of the stack is a left parenthesis for the last pop (not output):



The eighth character is an operator , which does not meet the condition of stack empty/priority is higher than the operator on the top of the stack  , and pops the stack until the condition is met



The ninth character is the operand , directly output:


The tenth character is an operator , which satisfies the condition that the stack is empty / priority is higher than the operator on the top of the stack  , and the operator is pushed onto the stack:


The eleventh character is the operand , directly output:


The twelfth character is an operator , which does not meet the condition of stack empty/priority higher than the operator on the top of the stack  , and pops the stack until the condition is met:




The thirteenth character is the operand , directly output:


After the traversal of the infix expression is completed , it is judged whether there are operators in the character stack , and if so, it is popped and output:


Conversion done:

3. Calculation of suffix expressions

Traverse each character of the suffix expression from left to right (need to prepare an operand stack to store operands and operation results)

1. Characters are operands :

Push directly into the stack ( Note: You need to analyze the complete operand and convert it to the corresponding data type)

2. The character is the operator :

Pop the stack twice in a row, use the two popped data to perform corresponding calculations, and push the calculation results into the stack

eg: the first operand popped out of the stack is a , the second operand popped out of the stack is b , and the operator at this time is - , then calculate ba  ( note: the order of a and b cannot be reversed), and put the result into stack.

3. Repeat the above steps until the postfix expression is traversed, and the data in the stack is the calculation result of the infix expression.

   Calculation of example        suffix expression 2 3 5 + * 7 1 / + 4 -    

Traverse each character of the suffix expression from left to right:

The first character is the operand , which is directly pushed onto the stack:


The second character is the operand , which is directly pushed onto the stack:


The third character is the operand , which is directly pushed onto the stack:


The fourth character is an operator , which is directly popped out of the stack twice:


Continue to pop:


Execution: the second pop operand operator   pops  the operand for the first time     

That is: 3 + 5

Result: 8

Push the calculation result onto the stack:


The fifth character is an operator , which is directly popped out of the stack twice:


Execution: the second pop operand operator   pops  the operand for the first time     

That is: 2 * 8

Result: 16

Push the calculation result onto the stack:


The sixth character is the operand , which is directly pushed onto the stack:


The seventh character is the operand , which is directly pushed onto the stack:


The eighth character is an operator , which is directly popped out of the stack twice:


Execution: the second pop operand operator   pops  the operand for the first time     

That is: 7 / 1

Results: 7

Push the calculation result onto the stack:


The ninth character is an operator , which is directly popped out of the stack twice:


Execution: the second pop operand operator   pops  the operand for the first time     

That is: 16 + 7

Result: 23

 Push the calculation result onto the stack:


The tenth character is the operand , which is directly pushed onto the stack:


The eleventh character is an operator , which is directly popped out of the stack twice:


Execution: the second pop operand operator   pops  the operand for the first time     

That is: 23 - 4

Results: 19

Push the calculation result onto the stack:


The suffix expression traversal is completed , and the data in the stack is the final calculation result:

4. Algorithm implementation

code:

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

#define ERROR 0
#define OK 1
#define STACK_INT_SIZE 10  /*存储空间初始分配量*/
#define STACKINCREMENT 5  /*存储空间分配增量*/
#define M 50

typedef char ElemType; /*定义字符数据类型*/
typedef double ElemType2;  /*定义运算数数据类型*/

/*字符栈*/
typedef struct{
    ElemType *base;
    ElemType *top;
    int stacksize; 
}SqStack;

/*运算数栈*/
typedef struct{
    ElemType2 *base;
    ElemType2 *top;
    int stacksize;
}NStack;

int InitStack(SqStack *S);   /*构造空栈*/
int push(SqStack *S,ElemType e); /*入栈*/
int Pop(SqStack *S,ElemType *e);  /*出栈*/
int StackEmpty(SqStack *s);  /*栈空判断*/
void in2post(ElemType *str,ElemType *p);  /*中缀表达式转后缀表达式*/
double cal_post(char *str);  /*计算后缀表达式*/

/*字符栈初始化*/
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;
}/*InitStack*/

/*运算数栈初始化*/
int InitStack_N(NStack *S){
    S->base=(ElemType2 *)malloc(STACK_INT_SIZE * sizeof(ElemType2));
    if(!S->base)
        return ERROR;
    S->top = S->base;
    S->stacksize = STACK_INT_SIZE;
    return OK;
}

/*字符栈入栈*/
int Push(SqStack *S,ElemType e){
    //判断栈满
    if(S->top - S->base >= S->stacksize){
        S->base = (ElemType *)realloc(S->base,(S->stacksize + STACKINCREMENT)*sizeof(ElemType));
        if(NULL == S->base)  //分配失败
            return ERROR;
        S->top = S->base + S->stacksize;
        S->stacksize = S->stacksize+STACKINCREMENT;
    }
    *S->top = e;
    S->top++;
    return OK;
}

/*运算数栈入栈*/
int Push_N(NStack *S,ElemType2 e){
    if(S->top - S->base >= S->stacksize){
        S->base = (ElemType2 *)realloc(S->base,(S->stacksize + STACKINCREMENT)*sizeof(ElemType2));
        if(NULL == S->base)
            return ERROR;
        S->top = S->base + S->stacksize;
        S->stacksize = S->stacksize+STACKINCREMENT;
    }
    *S->top = e;
    S->top++;
    return OK;
}

/*字符栈出栈*/
int Pop(SqStack *S,ElemType *e){
    //判断栈空
    if(S->top == S->base)
        return ERROR;
    S->top--;
    *e=*S->top;
    return OK;
}/*Pop*/

/*运算数栈出栈*/
int Pop_N(NStack *S,ElemType2 *e){
    if(S->top == S->base)
        return ERROR;
    S->top--;
    *e=*S->top;
    return OK;
}

/*判断栈空*/
int StackEmpty(SqStack *s){
    if(s->top == s->base)
        return OK;
    return ERROR;
}/*StackEmpty*/

//str为待转换的中缀表达式字符串,p为转换后的后缀表达式字符串
void in2post(ElemType *str,ElemType *p){   /*infix to postfix*/
    SqStack s;   
    InitStack(&s);   //初始化一个空字符栈
    ElemType e;
    int i;
    int j=0;
    for(i=0 ; i<strlen(str) ; i++)  //遍历中缀表达式
    {
        //遇到数字和小数点直接输出
        //使用循环完整接收一个运算数
        while(isdigit(str[i]) || '.'==str[i])
        {
            p[j++]=str[i++];
            if(!isdigit(str[i]) && '.'!=str[i])
                p[j++]=' ';   //一个数字完整输出后使用空格与其它运算符或数字分隔开
        }

        //遇到左括号直接入栈
        if('('==str[i])
            Push(&s,str[i]);

        //遇到右括号直接出栈,直到栈顶为左括号
        if(')'==str[i])
        {
            while('(' != *(s.top-1))
            {
                Pop(&s,&e);
                p[j++]=e;
                p[j++]=' ';
            }
            Pop(&s,&e);  //左括号出栈但不输出
        }

        //遇到+或—
        //1.栈空/栈顶为左括号:直接入栈
        //2.否则一直出栈,直到栈空/栈顶为左括号,再入栈
        if('+'==str[i] || '-'==str[i])
        {
            while(!StackEmpty(&s) && '('!=*(s.top-1))
            {
                Pop(&s,&e);
                p[j++]=e;
                p[j++]=' ';
            }
            Push(&s,str[i]);
        }

        //遇到*或/
        //1.栈空/栈顶为左括号/栈顶操作符为+ or -:直接入栈
        //2.否则一直出栈,直到满足1,再入栈
        if('*'==str[i] || '/'==str[i] || '%'==str[i])
        {
            while(!StackEmpty(&s) && '('!=*(s.top-1) && '+'!=*(s.top-1) && '-'!=*(s.top-1))
            {
                Pop(&s,&e);
                p[j++]=e;
                p[j++]=' ';
            }
            Push(&s,str[i]);
        }
    }
    //中缀表达式遍历完成,还需检查栈中是否有未输出字符
    //判断栈空,非空则直接出栈并输出(左括号不用输出)
    while(!StackEmpty(&s)){
        Pop(&s,&e);
        if('('!=e)
        {
            p[j++]=e;
            p[j++]=' ';
        }
    }
    p[--j]='\0';
}/*infix2postfix*/

//str为待计算的后缀表达式,返回值为计算结果
double cal_post(char *str){   /*计算后缀表达式*/
    int i;
    ElemType2 e,a,b;
    char d[M];
    NStack n;
    InitStack_N(&n);   //初始化一个运算数栈保存运算数
    for(i=0;i<strlen(str);i++)
    {
        int j=0;
        while(isdigit(str[i]) || '.'==str[i])
        {
            d[j++]=str[i++];
            d[j]='\0';
            if(!isdigit(str[i]) && '.'!=str[i])
            {
                e=atof(d);   //使用atof()将字符串形式的运算数转换为double型数据
                Push_N(&n,e);   //运算数入栈
            }
        }
        switch(str[i])
        {
            case '+':
                Pop_N(&n,&b);
                Pop_N(&n,&a);
                Push_N(&n,a+b);
                break;
            case '-':
                Pop_N(&n,&b);
                Pop_N(&n,&a);
                Push_N(&n,a-b);
                break;
            case '*':
                Pop_N(&n,&b);
                Pop_N(&n,&a);
                Push_N(&n,a*b);
                break;
            case '/':
                Pop_N(&n,&b);
                Pop_N(&n,&a);
                Push_N(&n,a/b);
                break;
        }
    }
    Pop_N(&n,&e);
    return e;
}/*calculate_postfix*/

int main()
{
    char str[M];
    char post[M];
    int i;
    printf("\n输入一串中缀表达式:\n");
    gets(str);
    printf("\n对应的后缀表达式:\n");
    in2post(str,post);
    printf("%s",post);
    printf("\n\n计算后缀表达式:\n");
    printf("%f",cal_post(post));
    return 0;
}

operation result:

5. Algorithm improvement

The prerequisites for the correct operation of the above code are:

① The input infix expression is legal

② The operand is non-negative

 For example  : input3*( -6 +5) and get the following result, the suffix expression and operation result are obviously incorrect.

The -6 here should be a complete operand, but the  '-' sign and the number '6' are separated in the suffix expression, and the obtained result is incorrect. Because the '-' sign in the code is always treated as an operator, it cannot analyze the complete and correct operand when facing a negative number like -6.

  • If you want to modify it, you must consider the case of negative numbers when performing operand analysis . When the sign '-' appears, it is necessary to judge whether it is used as a negative sign, a part of the operand, or as an operator.

Therefore, it is necessary to add negative number processing to the operand analysis code. The modified code is as follows:

In2post function code modification:

void in2post(ElemType *str,ElemType *p){   /*infix to postfix*/
    //初始化一个空栈
    SqStack s;
    InitStack(&s);
    ElemType e;

    int i;
    int j=0;
    for(i=0 ; i<strlen(str) ; i++)  //遍历中缀表达式
    {
        if('-' == str[i])    //负数情况判断
        {
            //表达式首位是'-',则一定是作为负数符号
            if(0 == i)
                p[j++]=str[i++];
            //'-'前面是'(',则一定是作为负数符号
            else if('(' == str[i-1])
                p[j++]=str[i++];
        }


        //遇到数字和小数点直接输出
        while(isdigit(str[i]) || '.'==str[i])
        {
            p[j++]=str[i++];
            if(!isdigit(str[i]) && '.'!=str[i])
                p[j++]=' ';   //一个数字完整输出后使用空格与其它运算符或数字分隔开
        }

        //遇到左括号直接入栈
        if('('==str[i])
            Push(&s,str[i]);

        //遇到右括号直接出栈,直到左括号出栈(左括号不输出)
        if(')'==str[i])
        {
            while('(' != *(s.top-1))
            {
                Pop(&s,&e);
                p[j++]=e;
                p[j++]=' ';
            }
            Pop(&s,&e);  //左括号出栈但不输出
        }

        //遇到+或—
        //1.栈空/栈顶为左括号:直接入栈
        //2.否则一直出栈,直到栈空/栈顶为左括号,再入栈
        if('+'==str[i] || '-'==str[i])
        {
            while(!StackEmpty(&s) && '('!=*(s.top-1))  //栈非空 且 栈顶非左括号
            {
                Pop(&s,&e);
                p[j++]=e;
                p[j++]=' ';
            }
            Push(&s,str[i]);
        }

        //遇到*或/
        //1.栈空/栈顶为左括号/栈顶操作符为+ or -:直接入栈
        //2.否则一直出栈,直到满足1,再入栈
        if('*'==str[i] || '/'==str[i] || '%'==str[i])
        {
            while(!StackEmpty(&s) && '('!=*(s.top-1) && '+'!=*(s.top-1) && '-'!=*(s.top-1))
            {
                Pop(&s,&e);
                p[j++]=e;
                p[j++]=' ';
            }
            Push(&s,str[i]);
        }
    }
    //中缀表达式遍历完成,还需检查栈中是否有未输出字符
    //判断栈空,非空则直接出栈并输出(左括号不用输出)
    while(!StackEmpty(&s)){
        Pop(&s,&e);
        if('('!=e)
        {
            p[j++]=e;
            p[j++]=' ';
        }
    }
    p[--j]='\0';
}

Cal_post function code modification:

double cal_post(char *str){
    ElemType2 e,a,b;
    char d[M];
    //初始化一个运算数栈保存运算数
    NStack n;
    InitStack_N(&n);
    int i=0;
    int j=0;
    while(str[i])  //遍历后缀表达式
    {
        switch(str[i])
        {
            case '-':
                if( isdigit(str[i+1]) )  //判断'-'是作为负数符号or运算符
                {
                    d[j++]=str[i++];  //将负号加入运算数字符串
                    d[j]='\0';
                    break;  //注:这里的break只是跳出switch循环
                }
                else
                {
                    Pop_N(&n,&b);
                    Pop_N(&n,&a);
                    Push_N(&n,a-b);
                    i++;
                    break;
                }
            case '+':
                Pop_N(&n,&b);
                Pop_N(&n,&a);
                Push_N(&n,a+b);
                i++;
                break;
            case '*':
                Pop_N(&n,&b);
                Pop_N(&n,&a);
                Push_N(&n,a*b);
                i++;
                break;
            case '/':
                Pop_N(&n,&b);
                Pop_N(&n,&a);
                Push_N(&n,a/b);
                i++;
                break;
            case ' ':i++;
        }

        //遇到运算数直接入栈(先转换double类型)
        //d保存后缀表达式中的字符串形式的运算数
        //使用atof将字符串转换为double类型
        while(isdigit(str[i]) || '.'==str[i])
        {
            d[j++]=str[i++];
            d[j]='\0';
            if( ' ' == str[i] )
            {
                e = atof(d);  //此时分析出的就是完整的运算数
                Push_N(&n,e);
                i++;
                j = 0;
            }
        }
    }
    Pop_N(&n,&e);
    return e;
}

  • operation result:

 

Guess you like

Origin blog.csdn.net/Amentos/article/details/127182926