c++ 中缀表达式转后缀表达式并计算值

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lbcab/article/details/51603048
  1. 什么是中缀表达式, 前缀表达式, 后缀表达式?

在数据结构中, 二叉树的遍历有三种(这里不考虑分层遍历等特殊需求): 前序遍历, 中序遍历和后序遍历. 如果将表达式看做一颗二叉树, 那么中缀表达式, 前缀表达式和后缀表达式就是这三种遍历的结果.

算数表达式本身就是中缀表达式, 也就是二叉树中序遍历的结果. 关于表达式二叉树是以运算符为根, 运算值为左右叶子.
例如表达式: 2 * 5 + 6 * (7 - 8) + 6

对应的二叉树为:

那么根据中序遍历(左中右), 前序遍历(中左右), 后序遍历(左右中)的规则可以得到对应的结果:
中序遍历: 2 * 5 + 6 * (7 - 8) + 6 , 即为表达式
前序遍历: ++*25*6-786
后序遍历:25*678-*+6+

从结果来看: 使用后序遍历可以计算表达式结果, 因为值为左右孩子, 操作符为根, 而后序遍历的输出顺序为左孩子, 右孩子, 根, 这样我们就可以读取前两个数通过第三个运算符计算结果.

2.中缀表达式转前后缀表达式
网上有一种简单的方式, 通过加括号来划分他们的作用域(就是标出以运算符为根的二叉树), 其实也是利用了计算式二叉树的特性—根为运算符, 值为左右孩子, 在加括号的过程中都是以运算符为中心, 将其左右孩子画在一起,这样将运算符提到对应的括号前就是前缀表达式, 之后就是后缀表达式.

以 2 * 5 + 6 * (7 - 8) + 6为例, 根据上面的二叉树来分析:
其实我们来看表达式中的(7 - 8) 对应的就是二叉树中的右下角以’-‘为根的子树.
操作步骤:
(1) 加括号标出每个以运算符为根的二叉树, 按照运算符的优先级对所有的运算单位加括号
(((2*5) + (6 * (7 - 8))) + 6)
(2)转换前缀表达式, 把运算符号移动到对应的括号前面
++((25) (6-(78)))6)
去掉括号即为前缀表达式: ++*25*6-786
(3)转换后缀表达式, 把运算符号移动到对应的括号后面
((25)(6(78)-)+6)+
去掉括号即为后缀表达式: 25*678-*+6+

其实我个人认为这种方式虽然简单但是操作起来很容易出错, 而且还是得需要画出对应的二叉树进行验证结果的正确性, 所以某不推荐这种方式, 个人推荐直接根据表达式, 画出对应的二叉树(因为以操作符为根节点, 所以根据中缀表达式即表达式本身就可以画出二叉树), 然后前序中序遍历即可得出对应的表达式.

3 中缀表达式转后缀表达式并计算值
如何使用程序来实现中缀表达式转后缀表达式?

程序实现思路
设置一个运算符的栈stack,从左只有扫描中缀表达式

(1) 如果遇到数字,直接放到后缀表达式尾;
(2) 如果遇到遇到运算符
a:若此时栈空,则直接入栈;
b:循环:若栈stack不空且当前运算符优先级小于或等于栈顶运算符的优先级,则将栈顶运算符依次出栈,置于后缀表达式尾;
c:若栈stack不空且当前运算符优先级大于栈顶运算符优先级,则将此运算符直接入栈;
d:若当前运算符为’(‘直接入栈.
e:若当前运算符为’)’则将栈顶元素出栈置于后缀表达式尾, 直到遇到运算符’(‘, 注意’(’ 与 ‘)’不用置于后缀表达式尾;

反复执行(1), (2), 直到整个中缀表达式扫描完毕, 若此时栈stack不为空, 则将栈顶运算符依次出栈置于后缀表达式尾.

此时就可以实现将中缀表达式转换成后缀表达式.

如何根据中缀表达式来计算表示式的值?
程序思路:
(1)将后缀表达式的值依次入栈, 如果当前值为操作符, 弹出栈顶的两个元素根据操作符计算结果, 然后再将结果压入栈中.
(2)循环(1)的操作, 最终栈中剩下的值即为计算结果.

参考程序(此程序只能计算四则运算+”( )”):

#include <iostream>
#include <stack>
#include <string>
using namespace std;

/**
 *判断当前字符是否为数字
 */
bool isNumber(char s) {
    string opers = "+-*/()";
    for (int i = 0; i < opers.length(); i++) {
        if (s == opers.at(i)) {
            return false;
        }
    }
    return true;
}

/**
 * 判断当前运算符与栈顶运算符的优先级大小
 */
bool isPriorityLow(char currOper, char topOper) {
    if (currOper == '+' || currOper == '-') {
        if (topOper == '*' || topOper == '/' || topOper == '+' || topOper == '-') {
            return true;
        }
    }

    if (currOper == '*' || currOper == '/') {
        if (topOper == '*' || topOper == '/') {
            return true;
        }
    }

    return false;
}

/**
 * 如果当前运算符优先级小于或等于栈顶运算符, 将栈顶运算符弹出加到后缀表达式尾,
 * 并继续进行比较, 直到当前运算符优先级大于栈顶运算符优先级
 */
void loopPushOperator(string& s, char oper, stack<char>& operators) {
    if (operators.size() > 0 && isPriorityLow(oper, operators.top())) {
        s += operators.top();
        operators.pop();
        loopPushOperator(s, oper, operators);
    }
}

/**
 * 判断运算符是压入运算符栈还是加到后缀表达式尾
 */
void handleOperator(string& s, char oper,  stack<char>& operators) {
    switch (oper) {
    case '+':
    case '-':
    case '*':
    case '/':
        //如果运算符栈为空, 直接将当前运算符压栈
        if (operators.size() <= 0) {
            operators.push(oper);
        //如果当前运算符优先级小于等于栈顶运算符优先级, 将栈顶运算符加到后缀表达式尾
        } else if (isPriorityLow(oper, operators.top())) {
            loopPushOperator(s, oper, operators);
            operators.push(oper);
        ///如果当前运算符优先级大于栈顶运算符优先级, 将当前运算符压栈
        } else {
            operators.push(oper);
        }
        break;
    case '(':
        operators.push(oper);  //当前运算符为'('直接压栈
        break;
    case ')':
        //将栈中元素弹出加到后缀表达式尾,直到遇到运算符"("
        while (operators.top() != '(') {
            s += operators.top();
            operators.pop();
        }
        operators.pop();
        break;
    default:
        break;
    }
}

/**
 * 中缀表达式转后缀表达式
 */
string infixToSuffix(string& s) {
    stack<char> operators;     //运算符栈
    string suffix;                        //后缀表达式

    for (int i = 0; i < s.length(); i++) {
        if (isNumber(s.at(i))) {   //如果是数字直接加到后缀表达式尾
            suffix += s.at(i);
        } else {
            handleOperator(suffix, s.at(i), operators);  //处理运算符
        }
    }

    if (suffix.length() > 0) {
        while (operators.size() > 0) {  //将运算符栈中留有的运算符全部出栈加到后缀表达式尾
            suffix += operators.top();
            operators.pop();
        }
        return suffix;
    } else {
        return "";
    }
}

/**
 * 根据运算符,计算栈顶两个数的值,并将计算的值压栈
 */
void CalculateResult( char oper, stack<int>& tmpStack) {
    if (tmpStack.size() < 2) {
        return;
    }
    //栈是先进后出,所以先弹出的是第二个值
    int secondVal = tmpStack.top();
    tmpStack.pop();
    int firstVal = tmpStack.top();
    tmpStack.pop();

    int result = 0;
    switch (oper) {
    case '+':
        result = firstVal + secondVal;
        break;
    case '-':
        result = firstVal - secondVal;
        break;
    case '*':
        result = firstVal * secondVal;
        break;
    case '/':
        result = firstVal / secondVal;
        break;
    default:
        break;
    }

    tmpStack.push(result);
}

/**
 * 通过后缀表达式计算结果
 * 将后缀表达式依次入栈, 如果为操作符, 弹出栈中两个元素计算结果再压入栈中
 */
float getResultUseSuffix(string& s) {
    if (s.length() <= 0) {
        return 0;
    }

    stack<int> tmpStack;
    for (int i = 0; i < s.length(); i++) {
        if (isNumber(s.at(i))) {
            tmpStack.push(s.at(i) - '0');
        } else {
            CalculateResult(s.at(i), tmpStack);
        }
    }
    return tmpStack.top();
}

int main() {
    string infix;
    //输入的字符串不能有空格
    cin>>infix;
    string suffix = infixToSuffix(infix);
    float result = getResultUseSuffix(suffix);
    cout<<"计算结果为: "<<result<<endl;
    return 0;
}

至此介绍完毕, 如果有什么问题, 希望留言指正.

展开阅读全文

没有更多推荐了,返回首页