"Estructura de datos": expresión sufija a expresión sufijo + cálculo de expresión sufijo

  • Se agregó un código para juzgar la legalidad de la expresión infija de entrada:

"Estructura de datos": juzgar la legalidad de las expresiones infijas - Amentos Blog - CSDN Blog

 

Tabla de contenido

1. Conceptos básicos

2. Expresión de infijo a expresión de sufijo

   Ejemplo La expresión infija 2*(3+5)+7/1-4 se convierte en una expresión postfija

3. Cálculo de expresiones de sufijo

   Ejemplo Cálculo de la expresión posfija 2 3 5 + * 7 1 / + 4 -

4. Implementación del algoritmo

5. Mejora del algoritmo


1. Conceptos básicos

1. Expresión infija:

        El operador está ubicado en el medio del operando en forma de infijo (como: 3+2), que es nuestro método de expresión común de fórmulas aritméticas y lógicas en la vida diaria.

2. Expresión de sufijo:

        También conocido como notación polaca inversa ( RPN ), el operador se coloca después de los dos operandos en forma de sufijo (por ejemplo: la expresión de sufijo de 3+2 es 3 2 +).

3. Expresión de prefijo:

        También conocido como Polaco ( Notación Polaca ), el operador se coloca delante de los dos operandos en forma de prefijo (por ejemplo: la expresión de prefijo de 3+2 es + 3 2).

Las expresiones infijas a menudo necesitan usar paréntesis para encerrar operadores y operandos correspondientes para indicar el orden de las operaciones.

ej.: 5*(2+1) Aunque la prioridad de * es mayor que la de + , la existencia de paréntesis significa que la operación + dentro de los paréntesis debe ejecutarse primero .  

Las expresiones infijas son adecuadas para la estructura del pensamiento humano y los hábitos informáticos, pero no para las computadoras.

Especialmente las expresiones infijas que contienen paréntesis son estructuras muy complejas para las computadoras.

Expresiones de postfijo para computadoras

A diferencia de las expresiones de infijo, las expresiones de sufijo no requieren paréntesis para indicar la precedencia del operador.

Los cálculos de las expresiones de posfijo se realizan secuencialmente en el orden en que aparecen los operadores  de izquierda a derecha (independientemente de la precedencia entre los operadores), que es una estructura relativamente simple para las computadoras. 

2. Expresión de infijo a expresión de sufijo

Recorra cada carácter de la expresión infija de izquierda a derecha (necesita preparar una pila de caracteres para almacenar operadores y paréntesis)

1. Los caracteres son operandos :  

Envíe directamente a la expresión de sufijo ( nota: primero se debe analizar el operando completo ).

2. Los caracteres son corchetes izquierdos :

Empújelo directamente a la pila ( nota: después de empujar el paréntesis izquierdo a la pila, la prioridad cae al mínimo ).

3. Los caracteres son paréntesis de cierre :

Extraiga la pila directamente y envíe los caracteres extraídos a la expresión de sufijo uno por uno hasta que el carácter superior de la pila sea un paréntesis izquierdo (el paréntesis izquierdo también se extrae de la pila, pero no se envía a la expresión de sufijo).

Resumen: siempre que la parte superior de la pila sea un paréntesis izquierdo, la pila se puede abrir por última vez.

4. El carácter es el operador :

Si la pila está vacía, se empuja directamente sobre la pila.

Si la pila no está vacía, juzgue al operador en la parte superior de la pila. Si la prioridad del operador en la parte superior de la pila es menor que la del operador, el operador será empujado hacia la pila; de lo contrario, la pila siempre aparecerá, y los caracteres extraídos se enviarán a la expresión de sufijo por turnos hasta que la pila esté vacía. O el operador en la parte superior de la pila tiene una prioridad más baja que este operador, y este operador se coloca en la pila.

Resumen: siempre que la pila esté vacía o la prioridad sea mayor que la del operador en la parte superior de la pila , se puede detener el estallido y se puede empujar al operador a la pila.

5. Repita los pasos anteriores hasta que se complete el recorrido de la expresión infijo, luego juzgue si la pila de caracteres está vacía, si no lo está, ábrala directamente y envíe los caracteres emergentes a la expresión sufijo a su vez.

Nota: Después de completar el recorrido de la expresión infija, es posible que todavía haya caracteres en la pila que no se hayan emitido, por lo que es necesario considerar que la pila está vacía.

   Ejemplo        de expresión infija 2*(3+5)+7/1-4 convertida a expresión postfija   

Recorra los caracteres de la expresión infija a su vez de izquierda a derecha:

El primer carácter es el operando , salida directa:


El segundo carácter es un operador , que cumple la condición de que la pila está vacía /la prioridad es mayor que la del operador en la parte superior de la pila , y el operador se coloca en la pila: 


El tercer carácter es un paréntesis izquierdo , que se inserta directamente en la pila ( después de que se inserta la pila, la prioridad se reduce al mínimo ):


El cuarto carácter es el operando , salida directa:


El quinto carácter es un operador , que cumple la condición de que la pila está vacía / la prioridad es mayor que la del operador en la parte superior de la pila  , y el operador se coloca en la pila:


El sexto carácter es el operando , salida directa:


El séptimo carácter es un paréntesis derecho , que se extrae directamente de la pila y se genera hasta que la parte superior de la pila es un paréntesis izquierdo para el último elemento emergente (no de salida):



El octavo carácter es un operador , que no cumple la condición de pila vacía/la prioridad es mayor que la del operador en la parte superior de la pila  , y extrae la pila hasta que se cumple la condición.



El noveno carácter es el operando , salida directa:


El décimo carácter es un operador , que cumple la condición de que la pila está vacía / la prioridad es mayor que la del operador en la parte superior de la pila  , y el operador se coloca en la pila:


El undécimo carácter es el operando , salida directa:


El duodécimo carácter es un operador , que no cumple la condición de pila vacía/prioridad mayor que el operador en la parte superior de la pila  , y extrae la pila hasta que se cumple la condición:




El decimotercer carácter es el operando , salida directa:


Una vez que se completa el recorrido de la expresión infija , se juzga si hay operadores en la pila de caracteres y, de ser así, se extrae y genera:


Conversión hecha:

3. Cálculo de expresiones de sufijo

Recorra cada carácter de la expresión de sufijo de izquierda a derecha (necesita preparar una pila de operandos para almacenar operandos y resultados de operaciones)

1. Los caracteres son operandos :

Empuje directamente a la pila ( Nota: debe analizar el operando completo y convertirlo al tipo de datos correspondiente)

2. El carácter es el operador :

Abre la pila dos veces seguidas, usa los dos datos emergentes para realizar los cálculos correspondientes y empuja los resultados del cálculo a la pila

por ejemplo: el primer operando que salió de la pila es a , el segundo operando que salió de la pila es b , y el operador en este momento es - , luego calcule ba  ( nota: el orden de a y b no se puede invertir), y poner el resultado en la pila.

3. Repita los pasos anteriores hasta que se atraviese la expresión del sufijo y los datos en la pila sean el resultado del cálculo de la expresión del infijo.

   Cálculo de       la expresión de sufijo de ejemplo 2 3 5 + * 7 1 / + 4 -    

Recorra cada carácter de la expresión de sufijo de izquierda a derecha:

El primer carácter es el operando , que se coloca directamente en la pila:


El segundo carácter es el operando , que se coloca directamente en la pila:


El tercer carácter es el operando , que se coloca directamente en la pila:


El cuarto carácter es un operador , que se saca directamente de la pila dos veces:


Continuar haciendo estallar:


Ejecución: el operador del segundo operando   abre  el operando por primera vez     

Es decir: 3 + 5

resultado: 8

Empuje el resultado del cálculo en la pila:


El quinto carácter es un operador , que se saca directamente de la pila dos veces:


Ejecución: el operador del segundo operando   abre  el operando por primera vez     

Es decir: 2 * 8

resultado: 16

Empuje el resultado del cálculo en la pila:


El sexto carácter es el operando , que se coloca directamente en la pila:


El séptimo carácter es el operando , que se coloca directamente en la pila:


El octavo carácter es un operador , que se saca directamente de la pila dos veces:


Ejecución: el operador del segundo operando   abre  el operando por primera vez     

Es decir: 7/1

Resultados: 7

Empuje el resultado del cálculo en la pila:


El noveno carácter es un operador , que se saca directamente de la pila dos veces:


Ejecución: el operador del segundo operando   abre  el operando por primera vez     

Es decir: 16 + 7

resultado: 23

 Empuje el resultado del cálculo en la pila:


El décimo carácter es el operando , que se coloca directamente en la pila:


El undécimo carácter es un operador , que se saca directamente de la pila dos veces:


Ejecución: el operador del segundo operando   abre  el operando por primera vez     

Es decir: 23 - 4

Resultados: 19

Empuje el resultado del cálculo en la pila:


El recorrido de la expresión de sufijo se completa y los datos en la pila son el resultado final del cálculo:

4. Implementación del algoritmo

código:

#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;
}

resultado de la operación:

5. Mejora del algoritmo

Los requisitos previos para el correcto funcionamiento del código anterior son:

① La expresión de infijo de entrada es legal

② El operando no es negativo

 Por ejemplo  : ingrese3*( -6 +5) y obtenga el siguiente resultado, la expresión del sufijo y el resultado de la operación son obviamente incorrectos.

El -6 aquí debería ser un operando completo, pero el signo  '-' y el número '6' están separados en la expresión del sufijo y el resultado obtenido es incorrecto. Debido a que el signo '-' en el código siempre se trata como un operador, no puede analizar el operando completo y correcto cuando se enfrenta a un número negativo como -6.

  • Si desea modificarlo, debe considerar el caso de números negativos al realizar el análisis de operandos . Cuando aparece el signo '-', es necesario juzgar si se usa como signo negativo , como parte del operando o como operador.

Por lo tanto, es necesario agregar el procesamiento de números negativos al código de análisis de operandos.El código modificado es el siguiente:

Modificación del código de la función In2post :

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';
}

Modificación del código de la función Cal_post :

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;
}

  • resultado de la operación:

 

Supongo que te gusta

Origin blog.csdn.net/Amentos/article/details/127182926
Recomendado
Clasificación