中缀表达式转后缀表达式

中缀表达式转后缀表达式(了解思路)

前面介绍了后缀表达式如何转中缀表达式,那么思考一下中缀表达式如何转后缀表达式

以下面的两个中缀表达式为例

  1. 不带括号的中缀表达式

2 + 3 * 4 - 5 / 6

  1. 带括号的中缀表达式

2 + 3 * (3 + 4 - 5) / 6

转后缀表达式基本思路与后缀表达式大体思路相反:

  1. 当遇到操作符时,操作符进栈
  2. 当遇到操作数时取操作符进行运算

但是需要考虑到下面的问题:

  1. 运算符优先级,什么时候该取操作符进行计算(不是单单遇到操作数就取操作符进行计算)
  2. 遇到括号需要提升优先级时应该如何处理

对于上面的两个问题提出两种解决方案:

  1. 处理运算符优先级时遵循两个原则
    1. 栈为空时,直接进操作符
    2. 栈不为空时,如果即将进入的操作符比当前栈内的操作符优先级高,那么就让该操作符进栈,而不是取操作符进行计算;如果即将进入的操作符比当前栈内的操作符优先级低或者相等,那么就可以让栈内当前操作符出栈进行计算,再让遇到的操作符进栈
  2. 遇到括号需要提升优先级可以采用递归子问题的方式解决,第一个原因是因为再递归中可以重现开辟一个栈,这个栈只需要存储当前括号内的操作符即可,第二个原因是因为括号内的表达式可以看作一个新的计算式,这个新的计算式也包括对应的运算符优先级,所以本质还是走第一个方案

具体分析如下图所示

📌

进行分析前的前置知识:

一个操作符的优先级不是由该操作符本身决定的,而应该是由相邻的操作符彼此直接优先级等级决定的。例如对于等式2+3*4,当计算时根据从左往右计算的规则,第一个看到的操作符是+,但是此时并不能进行计算,需要确定后面紧接的操作符是否比当前的+优先级高,很明显,*的优先级会高于+号,但是现在也不可以进行计算,因为*的操作符优先级的确比+高,但是并不确定紧挨着*的操作符的优先级是否比*高,接下来看*后面的操作符优先级,但是由于此时走到了算式结尾,所以现在可以确定*可以开始计算,当*计算完毕后则开始计算+

  • 考虑不带括号的计算式

基本思路实现代码:

class solution
{
public:
    //获取当前运算符优先级
    int getPriority(string& s1, string& top)
    {
        if ((s1 == "*" || s1 == "/") && (top == "+" || top == "-"))
        {
            return 1;
        }

        return 0;
    }

    //不带括号的中缀表达式转后缀表达式_直接打印后序遍历
    void MiddleExpreToRpnNonbraces(vector<string>& v)
    {
        stack<string> s;
        for (auto& str : v)
        {
            //操作符处理
            if ((str == "/" || str == "*" || str == "-" || str == "+") && (s.empty() || getPriority(str, s.top())))
            {
                s.push(str);
            }
            else
            {
                //可能存在当前操作符优先级小于栈顶操作符,出一次操作符后的栈顶操作符可能和当前操作符优先级相同,判断不止一次不能用if
                while (!s.empty() && (str == "/" || str == "*" || str == "-" || str == "+") && !getPriority(str, s.top()))
                {
                    cout << s.top() << " ";
                    s.pop();
                }
                //如果出现优先级相同的时候可能栈内数据全部出完,此时需要重新进一次数据
                if((str == "/" || str == "*" || str == "-" || str == "+") && s.empty())
                {
                    s.push(str);
                }
                if (!(str == "/" || str == "*" || str == "-" || str == "+"))
                {
                    cout << str << " ";
                }
            }
        }

        //栈内此时肯定还有操作符,所以需要依次弹出
        while (!s.empty())
        {
            cout << s.top() << " ";
            s.pop();
        }
    }
};

📌

上面的思路为基本的思路,但是存在一种优化思路,因为不考虑括号时,一共就4个操作符,而这四个操作符只存在*/+-优先级高,所以当前元素为*/时即可取出进行计算

优化后的代码:

class solution
{
public:
    //获取当前运算符优先级
    int getPriority(string& s1)
    {
        if (s1 == "*" || s1 == "/")
        {
            return 1;
        }

        return 0;
    }

    //不带括号的中缀表达式转后缀表达式_直接打印后序遍历
    void MiddleExpreToRpnNonbraces(vector<string>& v)
    {
        stack<string> s;
        for (auto& str : v)
        {
            //操作符处理
            if ((str == "/" || str == "*" || str == "-" || str == "+") && (s.empty() || getPriority(str)))
            {
                s.push(str);
            }
            else
            {
                //可能存在当前操作符优先级小于栈顶操作符,出一次操作符后的栈顶操作符可能和当前操作符优先级相同,判断不止一次不能用if
                while (!s.empty() && (str == "/" || str == "*" || str == "-" || str == "+") && !getPriority(str))
                {
                    cout << s.top() << " ";
                    s.pop();
                }
                //如果出现优先级相同的时候可能栈内数据全部出完,此时需要重新进一次数据
                if((str == "/" || str == "*" || str == "-" || str == "+") && s.empty())
                {
                    s.push(str);
                }
                if (!(str == "/" || str == "*" || str == "-" || str == "+"))
                {
                    cout << str << " ";
                }
            }
        }

        //栈内此时肯定还有操作符,所以需要依次弹出
        while (!s.empty())
        {
            cout << s.top() << " ";
            s.pop();
        }
    }
};
  • 考虑带括号的计算式

class solution
{
public:
    //获取当前运算符优先级
    int getPriority(const string& s1, const string& top)
    {
        if (((s1 == "(" && (top == "*" || top == "/") && (top != "+" || top != "-")))
            || ((s1 == "(" && (top != "*" || top != "/") && (top == "+" || top == "-")))
            || (s1 != "(" && (s1 == "*" || s1 == "/") && (top == "+" || top == "-")))
        {
            return 1;
        }

        return 0;
    }

    //带括号的中缀表达式转后缀表达式_直接打印后序遍历
    void MiddleExpreToRpn(vector<string>& v, size_t& i)
    {
        stack<string> s;
        for (; i < v.size(); i++)
        {
            //处理操作符
            if ((v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && (s.empty() || getPriority(v[i], s.top())))
            {
                s.push(v[i]);
            }
            //当遇到左括号时
            else if (v[i] == "(")//左括号进入递归操作符栈
            {
                i++;
                //需要在递归中同时改变i的值
                MiddleExpreToRpn(v, i);
            }
            //当遇到右括号时结束递归
            else if (v[i] == ")")
            {
                //结束递归前需要对栈内操作符进行处理
                while (!s.empty())
                {
                    cout << s.top() << " ";
                    s.pop();
                }

                return;
            }
            else
            {
                //可能存在当前操作符优先级小于栈顶操作符,出一次操作符后的栈顶操作符可能和当前操作符优先级相同,判断不止一次不能用if
                while (!s.empty() && (v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && !getPriority(v[i], s.top()))
                {
                    cout << s.top() << " ";
                    s.pop();
                }
                //如果出现优先级相同的时候可能栈内数据全部出完,此时需要重新进一次数据
                if ((v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && s.empty())
                {
                    s.push(v[i]);
                }
                //可能存在一个操作符优先级高于当前栈顶的操作符
                if ((v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+") && (s.empty() || getPriority(v[i], s.top())))
                {
                    s.push(v[i]);
                }
                if (!(v[i] == "/" || v[i] == "*" || v[i] == "-" || v[i] == "+"))
                {
                    cout << v[i] << " ";
                }
            }
        }

        //栈内此时肯定还有操作符,所以需要依次弹出
        while (!s.empty())
        {
            cout << s.top() << " ";
            s.pop();
        }
    }
};
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怡晗★

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值