洛谷-P1175 表达式的转换


题目

Problem Description

平常我们书写的表达式称为中缀表达式,因为它将运算符放在两个操作数中间,许多情况下为了确定运算顺序,括号是不可少的,而中缀表达式就不必用括号了。

后缀标记法:书写表达式时采用运算紧跟在两个操作数之后,从而实现了无括号处理和优先级处理,使计算机的处理规则简化为:从左到右顺序完成计算,并用结果取而代之。

例如:8–(3+2*6)/5+4可以写为:8 3 2 6*+5/–4+

其计算步骤为:8 3 2 6 * + 5 / – 4 +

8 3 12 + 5 / – 4 +

8 15 5 / – 4 +

8 3 – 4 +

5 4 +

9

编写一个程序,完成这个转换,要求输出的每一个数据间都留一个空格。

 

Input

就一行,是一个中缀表达式。输入的符号中只有这些基本符号“0123456789+-*/^()”,并且不会出现形如2*-3的格式。

表达式中的基本数字也都是一位的,不会出现形如12形式的数字。

所输入的字符串不要判错。

 

Output

若干个后缀表达式,第I+1行比第I行少一个运算符和一个操作数,最后一行只有一个数字,表示运算结果。
 

Sample Input

 

8–(3+2*6)/5+4

 

 

Sample Output

8 3 2 6 * + 5 / – 4 + 
8 3 12 + 5 / – 4 + 
8 15 5 / – 4 + 
8 34 + 
5 4 + 
9

 


题解

  本题涉及到前缀、中缀、后缀表达式的转换,部分参考资料来自百度百科……

中缀表达式(中缀记法)

  中缀表达式(或中缀记法)是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。与前缀表达式(例:+ 3 4)或后缀表达式(例:3 4 +)相比,中缀表达式不容易被计算机解析,但仍被许多程序语言使用,因为它符合人们的普遍用法。与前缀或后缀记法不同的是,中缀记法中括号是必需的。计算过程中必须用括号将操作符和对应的操作数括起来,用于指示运算的次序。

前缀表达式(前缀记法、波兰式)

  前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3)。

后缀表达式(后缀记法、逆波兰式)

  后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。

后缀表达式的转换

  该题的第一个步骤就是将一个中缀表达式转换为后缀表达式,可以按以下步骤来进行,但是如果有负数存在的话,可能有一点小小的改动。
  (1)init两个栈:运算符栈Op和储存过程结果的栈R;
  (2)一个一个字符的遍历输入的表达式,直到遍历到表达式的最右边;
    2.1)如果遇到数,直接入栈R,此时注意负数的判断;
    2.2)如果遇到运算符,比较其与运算符栈Op栈顶运算符的优先级:
      如果运算符栈Op为空,或运算符栈Op栈顶运算符为左括号'(',或该运算符的优先级比栈顶运算符的高,则直接将此运算符入栈;
      否则,将运算符栈Op栈顶的运算符弹出并压入到栈R中,再次比较其与运算符栈Op栈顶运算符的优先级;
    2.3) 如果遇到括号:
      如果遇到左括号'(',则直接压入运算符栈Op;
      如果遇到右括号')',则依次弹出运算符栈Op栈顶的运算符,并压入栈R,直到遇到左括号为止,并丢弃这对括号;
  (3)将运算符栈Op中剩余的运算符依次弹出并压入栈R;
  (4)依次弹出栈R中的元素并逆序输出。

  例如,将输入样例中的中缀表达式8–(3+2*6)/5+4'转换为后缀表达式的过程如下:

遍历时的元素 运算符栈Op(top->bottom) 栈R(top->bottom) 步骤
8 empty 8 2.1
- - 8 2.2
( ( - 8 2.3
3 ( - 3 8 2.1
+ + ( - 3 8 2.2
2 + ( - 2 3 8 2.1
* * + ( - 2 3 8 2.2 *优先级比+高
6 * + ( - 6 2 3 8 2.1
) - + * 6 2 3 8 2.3 依次弹出运算符栈Op栈顶的运算符
/ / - + * 6 2 3 8 2.2 /优先级比-高
5 / - 5 + * 6 2 3 8 2.1
+ + - / 5 + * 6 2 3 8 2.2 +优先级比/低,不比-高,栈空
4 + 4 - / 5 + * 6 2 3 8 2.1
\0 empty + 4 - / 5 + * 6 2 3 8 3

  最后的输出为'8 3 2 6 * + 5 / - 4 +'(注意需要逆序输出)。

后缀表达式的求值

  该题的第二个步骤就是逐步计算一个后缀表达式,可以按以下步骤来进行:
  (1)init两个栈:储存数据的栈Op(名字不太好,但可以用上面步骤的栈)和储存过程结果的栈R;
  (2)从Op弹出元素,直到Op为空;
    2.1)如果遇到数,直接入栈R;
    2.2)如果遇到运算符,用运算符对栈顶元素和次顶元素做相应的计算(次顶元素 运算符 栈顶元素),然后输出:

栈Op(top->bottom) 栈R(top->bottom)
8 3 2 6 * + 5 / - 4 + empty
* + 5 / - 4 + 6 2 3 8
+ 5 / - 4 + 12 3 8
5 / - 4 + 15 8
 / - 4 + 5 15 8
- 4 + 3 8
4 + 5
+ 4 5
empty 9

 

  了解了相关知识,用栈就很好实现了,我认真写了一个栈……还是为了数据结构的考试……


代码

#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <cmath>
#include <map>

#define STACK_SIZE 100
#define STACKINCREMENT 10
#define OK 1
#define ERROR 0
typedef struct myElemType{
    bool isOperator;
    int data;
}ElemType;
typedef int Status;

using namespace std;

class Stack{
public:
    //Status InitStack(void) //数据结构的写法
    void InitStack(void){
        bottom=(ElemType*)malloc(STACK_SIZE*sizeof(ElemType));
        //if(!bottom)
        //return ERROR;
        top=bottom;
        stacksize=STACK_SIZE;
        //return OK;
    }
    //Status push(ElemType e) //数据结构的写法
    void push(ElemType e){
        *top=e;
        top++;
        if(top-bottom==stacksize){
            bottom=(ElemType*)realloc(bottom,(STACKINCREMENT+stacksize)*sizeof(ElemType));
            //if(!bottom)
            //return ERROR;
            top=bottom+stacksize;
            stacksize+=STACKINCREMENT;
        }
        //return OK;
    }
    Status pop(ElemType *e){
        if(top==bottom)
            return ERROR;
        top--;
        *e=*top;
        return OK;
    }
    ElemType* Top(void){
        if(top==bottom)
            return NULL;
        return top-1;
    }

    void printTtoB(void){
        if(top==bottom)
            return;
        for(ElemType* i=top-1;i!=bottom;i--){
            if(i->isOperator==0)
                cout << i->data << " ";
            else
                cout << (char)i->data << " ";
        }
        if(bottom->isOperator==0)
            cout << bottom->data;
        else
            cout << (char)(bottom->data);
    }
    void printBtoT(void){
        if(top==bottom)
            return;
        for(ElemType* i=bottom;i!=top-1;i++){
            if(i->isOperator==0)
                cout << i->data << " ";
            else
                cout << (char)i->data << " ";
        }
        if((top-1)->isOperator==0)
            cout << (top-1)->data;
        else
            cout << (char)((top-1)->data);
    }
    void DestroyStack(void){
        do{
            free(top);
            top--;
        }while(top!=bottom);
        top=NULL;
        bottom=NULL;
        stacksize=0;
    }
private:
    ElemType *bottom;
    ElemType *top;
    int stacksize;
};

Stack Op,R;

int main(){
    map <char,int> priority;  //定义符号的优先级
    priority['^']=3;
    priority['*']=2;
    priority['/']=2;
    priority['+']=1;
    priority['-']=1;
    ElemType temp;
    char c[105],o;
    int num=0;
    bool isnum=1,isminus=0;
    Op.InitStack();
    R.InitStack();
    cin >> c;
    for(int i=0;i<strlen(c);i++){
        if(c[i]>='0' && c[i]<='9'){  //2.1)如果遇到数,直接入栈R,此时注意负数的判断;
            isnum=0;
            if(!isminus)  //负数的判断
                num=num*10+c[i]-'0';
            else
                num=num*10-c[i]+'0';
            if(i==strlen(c)-1 || c[i+1]<'0' || c[i+1]>='9'){
                temp.data=num;
                temp.isOperator=0;
                R.push(temp);
                num=0;
                isminus=0;
            }
        }
        else if(c[i]=='('){  //如果遇到左括号'(',则直接压入运算符栈Op;
            temp.data=(int)c[i];
            temp.isOperator=1;
            Op.push(temp);
            isnum=1;
            isminus=0;
        }
        else if(c[i]==')')  //如果遇到右括号')',则依次弹出运算符栈Op栈顶的运算符,并压入栈R,直到遇到左括号为止,并丢弃这对括号;
            while(Op.pop(&temp) && temp.data!='(')
                R.push(temp);
        else{ //2.2)如果遇到运算符,比较其与运算符栈Op栈顶运算符的优先级
            if(isnum && c[i]=='-')  //负数的判断
                isminus=1;
            else{
                while(Op.Top()!=NULL && Op.Top()->data!='(' && priority[((char)(Op.Top()->data))]>=priority[c[i]]){  //如果运算符栈Op为空,或运算符栈Op栈顶运算符为左括号'(',或该运算符的优先级比栈顶运算符的高,则直接将此运算符入栈;
                    Op.pop(&temp);
                    R.push(temp);
                }
                temp.data=(int)c[i];
                temp.isOperator=1;
                Op.push(temp);
            }
            isnum=1;
        }
    }
    while(R.pop(&temp))
        Op.push(temp);
    Op.printTtoB();
    cout << endl;
    while(Op.Top()!=NULL){  //(2)从Op弹出元素,直到Op为空;
        Op.pop(&temp);
        if(temp.isOperator){  //2.2)如果遇到运算符,用运算符对栈顶元素和次顶元素做相应的计算(次顶元素 运算符 栈顶元素),然后输出:
            o=(char)(temp.data);  //运算符
            R.pop(&temp);
            num=temp.data;  //栈顶元素
            R.pop(&temp);  //次顶元素
            if(o=='+')
                temp.data+=num;
            else if (o=='-')
                temp.data-=num;
            else if (o=='*')
                temp.data*=num;
            else if (o=='/')
                temp.data/=num;
            else if (o=='^')
                temp.data=(int)pow(temp.data,num);
            R.push(temp);
            R.printBtoT();
            cout << " ";
            Op.printTtoB();
            cout << endl;
        }
        else //2.1)如果遇到数,直接入栈R;
            R.push(temp);
    }
    return 0;
}

 

posted @ 2017-10-10 02:20  HRay-_-  阅读(868)  评论(0)    收藏  举报