算术表达式的逆波兰表示与计算

发布于 2023-05-22  301 次阅读


一、设计目的

将程序设计语言中的算术表达式转换为用逆波兰表示的形式,并计算逆波兰表示的表达式的值。

二、设计内容

逆波兰表示形式是把运算对象写在前面,把运算符写在后面,用这种方法表示的表达式也称作后缀式。逆波兰表示形式的特点在于运算对象顺序不变,运算符的位置反映运算顺序。采用逆波兰式可以很好的表示简单算术表达式,优点在于易于计算机处理。

1.逆波兰式生成的思想及算法

(1)构造一个运算符栈,栈顶算符的优先级较高;
(2)读入一个用中缀形式表示的简单算术表达式,以“#”结束;
(3)从左至右扫描该表达式,从第一个字符开始,读入每个字符:
①如果该字符是运算数,则分析到该运算数的结束并将该运算数直接输出;
②如果是运算符,则将该算符与栈顶运算符的优先关系进行比较:若优先级高于栈顶运算符,则将该算符入栈,否则将栈顶算符出栈,该算符入栈;

重复上述步骤直至扫描完整个表达式,确定所有字符都得到正确处理,便可将中缀形式的表达式转化为逆波兰形式表示的表达式。

2.逆波兰式的计算方法

(1)构造一个栈,存放运算对象;
(2)读入逆波兰形式的算术表达式;
(3)自左至右扫描该表达式,判断读入的每个字符:
①如果该字符是运算数,则将该字符入栈;
②如果是运算符,若是双目运算符,则弹出栈顶 2 个元素进行该运算,结果入栈;是单目运算符,则弹出栈顶 1 个元素进行该运算,结果入栈;
重复以上过程直至扫描完整个表达式。

三、扩充功能

若输入表达式含有实数,编程实现逆波兰式的生成和计值。

四、算法实现

1.算法流程图

2.程序代码

#include <iostream>
#include <algorithm>
#include <iomanip>
#include <Windows.h>
#include <stdio.h>
#include <ctype.h>
#include <stack>
#include <ctype.h>
#include <map>
using namespace std;
#define INITIAL SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); // 字体恢复原来的颜色
#define GREEN SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
#define YELLOW SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
#define PURPLE SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0d);
int Sample = 1;             //
int i_all;                  //字符表长度
double ANS = 0;             //结果
stack<double> Num;          //运算数栈
stack<char> Ope;            //运算符栈
string Notation[255];       //字符表
string token;               //中缀表达式
string note;                //数字转存
map<char, int> Opera;       //优先级
void ReversePolishNotation()
{
    PURPLE
    cout << "Sample-" << Sample << endl;
    Sample++;
    while(!Ope.empty()) Ope.pop();
    Ope.push('#');
    i_all = 1;
    note = "";
    for(int i = 0 ; i < token.size() ; i++)
    {
        if (token[i] == ' ' || token[i] == '\t')        //过滤空格和Tab
        {
            continue;
        }
        if(isdigit(token[i]) || token[i] == '.')                 //当前位是数字
        {
            note.push_back(token[i]);
            if(!isdigit(token[i+1]) && token[i+1] != '.')        //下一位既不是数字,也不是小数点
            {
                Notation[i_all] = note;     //将数字转存
                i_all++;                    //中间表长度+1
                //cout << note << endl;
                note = "";                  //Note初始化
            }
        }
        else                                //当前位不是数字
        {
            if(token[i] == '(')             //左括号,入栈
            {
                Ope.push(token[i]);
            }
            else if(token[i] == ')')             //右括号,出栈到匹配上一个左括号
            {
                while(TRUE)
                {
                    if(Ope.top() == '(')
                    {
                        Ope.pop();
                        break;
                    }
                    else
                        Notation[i_all] = Ope.top();
                        Ope.pop();
                        i_all++;
                }
            }
            else                                //其他运算符
            {
                if(Opera[token[i]] > Opera[Ope.top()])      //如果优先级高,入栈
                {
                    Ope.push(token[i]);
                }
                else                                        //否则栈顶运算符出栈,该运算符入栈
                {
                    Notation[i_all] = Ope.top();
                    Ope.pop();
                    i_all++;
                    Ope.push(token[i]);
                }
            }
        }
    }
    while(!Ope.empty())                 //将余下运算符出栈
    {
        Notation[i_all] = Ope.top();
        Ope.pop();
        i_all++;
    }
    GREEN
    for(int i = 1 ; i < i_all -1; i++)
    {
        cout << Notation[i] << " ";
    }
    cout << endl;
    token = "";
}
void Compute()
{
    ANS = 0;
    while(!Num.empty()) Num.pop();
    for(int i = 1 ; i < i_all -1 ; i++)
    {
        if(isdigit(Notation[i][0]))                 //当前元素是一个数
        {
            //cout << "This is  Num: " << Notation[i] << endl;
            Num.push(stold(Notation[i]));           //入栈
        }
        else                                        //运算符,取数计算
        {
            ANS = 0;
            double An, Bn;
            Bn = Num.top();
            Num.pop();
            An = Num.top();
            Num.pop();
            if(Notation[i] == "+")
            {
                ANS = An + Bn;
            }
            else if(Notation[i] == "-")
            {
                ANS = An - Bn;
            }
            else if(Notation[i] == "*")
            {
                ANS = An * Bn;
            }
            else if(Notation[i] == "/")
            {
                ANS = An / Bn;
            }
            //cout << "This is ANS: " << ANS << endl;
            Num.push(ANS);          //计算完成,结果压回栈里
        }
    }
    YELLOW
    cout << "The ans is: " << setprecision(12) << ANS << endl << endl;
}
void Read(FILE *fp)
{
    char ch;
    while (TRUE)
    {
        ch = fgetc(fp);
        if (ch == '\n' || ch == EOF)
        {
            if (token != "")
            {
                ReversePolishNotation();
                Compute();
            }
        }
        else
        {
            token.push_back(ch); // 将读取的字符ch存入token中
        }
        if(ch == EOF)
            break;
    }
}
int main()
{
    Opera['/'] = 2;         //算数优先级排布
    Opera['*'] = 2;
    Opera['-'] = 1;
    Opera['+'] = 1;
    Opera['#'] = 0;
    Opera['('] = 0;
    Opera[')'] = 0;
    string filename; // 文件路径
    FILE *fp;        // 文件指针
    cout << "Please enter the path: ";
    while (true)
    {
        cin >> filename;
        fp = fopen(filename.c_str(), "r");
        if (fp != NULL)
            break;
        else
            cout << "Wrong Path, Please try again" << endl;
    }
    Read(fp);
    fclose(fp);
    INITIAL
    system("pause");
    return 0;
}