zoukankan      html  css  js  c++  java
  • 中缀表达式转换为前缀表达式

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

             在《前缀表达式的计算》中,我们讨论了对前缀表达式如何计算:设置一个操作数栈,对前缀表达式从右到左扫描,遇到操作数直接入栈,遇到操作符则从操作数栈弹栈,先弹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。

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

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

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

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

     

    中缀转前缀

    中缀转后缀

    操作符栈

    操作符栈

    扫描顺序

    从右到左

    从左到右

    遇到操作数

    直接归入

    直接归入

    遇到右括号

    直接入栈

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

    遇到左括号

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

    直接入栈

    遇到其他操作符

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

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

    操作符栈中的优先级

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

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

    是否翻转

    翻转

    无需翻转

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

             总结

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

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

  • 相关阅读:
    WordPress主题开发实例:查询单篇文章
    WordPress基础:get_page_link获取页面地址
    WordPress主题开发:优化标题
    React中的的JSX
    WordPress主题开发:WP_Query常用参数
    WordPress基础:wp_list_pages显示页面信息列表
    WordPress基础:常用分类列表wp_list_categories
    SQL Server 2008 安装教程
    XCODE调试时不显示变量值/指针地址的解决方案
    android iOS 编码问题害死人!
  • 原文地址:https://www.cnblogs.com/unixfy/p/3344550.html
Copyright © 2011-2022 走看看