中缀表达式转换为前缀表达式

时间:2022-01-10 18:47:06

中缀表达式转换为前缀表达式

         在《前缀表达式的计算》中,我们讨论了对前缀表达式如何计算:设置一个操作数栈,对前缀表达式从右到左扫描,遇到操作数直接入栈,遇到操作符则从操作数栈弹栈,先弹left值后弹right值,根据操作符进行相应的计算,并将计算结果压入到操作数栈中,最终将整个前缀表达式扫面完毕。这时操作数栈中只有一个元素,该元素的值即为前缀表达式的值。

         在《中缀表达式转换为后缀表达式》中,我们讨论了如何将一个中缀表达式转换为其对应的后缀表达式。其思想为:设置一个操作符栈,如果遇到操作数,则直接将操作数放进后缀表达式中,如果遇到非操作数,则:如果是左括号,则将左括号入栈;如果是右括号,则从操作符栈中将操作符弹栈,放入后缀表达式中,直至栈空或遇到栈中的左括号,并将左括号弹栈;如果是其他操作符,则比较其优先级与栈中操作符优先级情况,如果栈中的操作符的优先级大于等于当前操作符,则将栈中操作符弹栈,直至栈空,或栈中操作符优先级小于当前操作符的优先级,将当前操作符压栈。当从左到右顺序扫描完整个中缀表达式后,检测操作符栈,如果非空,则依次弹栈,将弹出的操作符依次压入到后缀表达式中。最终,得到中缀表达式对应的后缀表达式。如果还想计算后缀表达式的值,则可以参考《后缀表达式的计算》。

         本文我们是讨论如何将中缀表达式转换为前缀表达式。

         我们先给出中缀表达式转换前缀表达式的程序,然后再对程序进行相关讲解,之后在与中缀表达式转换后缀表达式的过程进行比较,分析其中的差异存在于哪里。

// 中缀表达式转换为前缀表达式
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <stack>
#include <algorithm>
using namespace std;

void GetInfix(vector<string>& infix)
{
    infix.clear();
    string line;
    getline(cin, line);

    istringstream sin(line);
    string tmp;
    while (sin >> tmp)
    {
        infix.push_back(tmp);
    }
}

// 初始化操作符
void InitOperators(map<string, int>& opers)
{
    opers.clear();
    opers["("] = 100;
    opers[")"] = 900;
    opers["+"] = 100;
    opers["-"] = 100;
    opers["*"] = 200;
    opers["/"] = 200;
}

bool IsOperator(const string& op, const map<string, int>& opers)
{
    auto cit = opers.find(op);
    if (cit != opers.end())
    {
        return true;
    }
    else
    {
        return false;
    }
}

void InfixToPrefix(const vector<string>& infix, vector<string>& prefix, map<string, int>& opers)
{
    prefix.clear();
    stack<string> stk; // 操作符栈
    for (int i = infix.size() - 1; i >= 0; --i) // 从右到左扫描
    {
        if (!IsOperator(infix[i], opers)) // 如果是操作数
        {
            prefix.push_back(infix[i]);
        }
        else // 如果是操作符
        {
            if (infix[i] == ")") // 如果是右括号,则直接入栈
            {
                stk.push(infix[i]);
            }
            else if (infix[i] == "(") // 如果是左括号
            {
                // 依次弹出栈中的操作符,直至遇到右括号
                while (!stk.empty())
                {
                    if (stk.top() == ")")
                    {
                        stk.pop();
                        break;
                    }
                    else
                    {
                        prefix.push_back(stk.top());
                        stk.pop();
                    }
                }
            }
            else // 如果是其他操作符
            {
                while (!stk.empty() && stk.top() != ")" && opers[stk.top()] > opers[infix[i]]) // 栈顶操作符优先级大于当前操作符优先级
                {
                    prefix.push_back(stk.top());
                    stk.pop();
                }
                // 将当前操作符入栈
                stk.push(infix[i]);
            }
        }
    }

    // 检测操作符栈是否为空
    while (!stk.empty())
    {
        prefix.push_back(stk.top());
        stk.pop();
    }
    // 将prefix翻转
    reverse(prefix.begin(), prefix.end());
    return;
}

void Display(const vector<string>& fix)
{
    for (auto i = 0; i != fix.size(); ++i)
    {
        cout << fix[i] << ' ';
    }
    cout << endl;
}

int main()
{
    map<string, int> opers;
    InitOperators(opers);

    while (true)
    {
        vector<string> infix, prefix;
        GetInfix(infix);

        Display(infix);

        InfixToPrefix(infix, prefix, opers);
        Display(prefix);
        cout << endl;
    }
    return 0;
}

中缀表达式转换为前缀表达式

         首先说明的是,我们的中缀表达式输入是用空白符间隔的,而没有对中缀表达式进行词法分析,对中缀表达式的词法分析可以参考《四则运算的词法分析》。

         我们首先实现了中缀表达式的输入、操作符及其优先级的初始化、判断是否为操作符。然后重点在中缀表达式转换为前缀表达式的函数:InfixToPrefix。

         中缀表达式转换前缀表达式的操作过程为:

         首先设定一个操作符栈,从右到左顺序扫描整个中缀表达式,如果是操作数,则直接归入前缀表达式;如果是操作符,则检测器是否是右括号,如果是右括号,则直接将其入栈;如果是左括号,则将栈中的操作符依次弹栈,归入前缀表达式,直至遇到右括号,将右括号弹栈,处理结束;如果是其他操作符,则检测栈顶操作符的优先级与当前操作符的优先级关系,如果栈顶操作符优先级大于当前操作符的优先级,则弹栈,并归入前缀表达式,直至栈顶操作符优先级小于等于当前操作符优先级,这时将当前操作符压栈。

         当扫描完毕整个中缀表达式后,检测操作符栈是否为空,如果不为空,则依次将栈中操作符弹栈,归入前缀表达式。最后,将前缀表达式翻转,得到中缀表达式对应的前缀表达式。

         下面,我们结合中缀表达式转后缀表达式的过程,比较中缀转前缀与中缀转后缀的联系和区别。

 

中缀转前缀

中缀转后缀

操作符栈

操作符栈

扫描顺序

从右到左

从左到右

遇到操作数

直接归入

直接归入

遇到右括号

直接入栈

将栈中操作符依次弹栈,归入,直至遇到左括号,将左括号弹栈,处理完毕

遇到左括号

将栈中操作符依次弹栈,归入,直至遇到右括号,将右括号弹栈,处理完毕

直接入栈

遇到其他操作符

检测栈顶操作符优先级与当前操作符优先级关系,如果栈顶大于当前,则出栈,归入,直至栈顶小于等于当前,并将当前操作符入栈

检测栈顶与当前优先级关系,如果栈顶大于等于当前则出栈,归入,直至栈顶小于当前,并将当前操作符入栈

操作符栈中的优先级

从栈底到栈顶操作优先级:非递减。即:栈顶可以大于或等于下面的

从栈底到栈顶优先级:递增。即:栈顶必须大于下面的

是否翻转

翻转

无需翻转

 

         通过上表,我们可以看出中缀转前缀与中缀转后缀的最大区别在于两点:扫描顺序和操作符栈中操作符优先级的排列关系。

 

         总结

         以上我们主要讨论了中缀表达式转换前缀表达式的过程,并与中缀表达式转换后缀表达式进行了比较,找出其中的差异点。通过本文,将中缀表达式转换为前缀表达式,以及之前有《前缀表达式的计算》,这样我们可以通过前缀表达式,计算中缀表达式的值了。即先将中缀表达式转换为前缀表达式,然后对前缀表达式进行计算,计算结果即为中缀表达式的值。

         下一步,我们将讨论如何将前缀表达式转换中缀表达式,如何将后缀表达式转换为中缀表达式,以及前缀表达式和后缀表达式之间的直接相互转换。