计算器——可支持小数的任意四则运算(中缀表达式转为后缀表达式算法)

中缀表达式转为后缀表达式的原理过程主要包括以下步骤:

1. 初始化两个栈,一个用于存储操作数,一个用于存储运算符。
2. 从左到右扫描中缀表达式的每个字符。
3. 如果遇到数字,则直接将其压入操作数栈。
4. 如果遇到运算符,则分两种情况处理:如果运算符优先级大于等于栈顶运算符的优先级,则将栈顶运算符弹出并压入后缀表达式,直到栈为空或者栈顶运算符的优先级低于当前运算符为止,然后将当前运算符压入栈;如果运算符优先级小于栈顶运算符的优先级,则直接将当前运算符压入栈。
**5.**当表达式扫描完毕后,如果栈中仍有剩余的运算符,则将这些运算符依次弹出并压入后缀表达式。

**6.**最后,后缀表达式中剩余的元素即为转换后的结果。

需要注意的是,在实际应用中,可能还需要进行一些额外的处理,比如补全缺失的括号,以确保表达式的正确性。

(括号法)

完整代码(注释都在代码中):

cpp 复制代码
#include <iostream>//用于输入输出操作
#include <stack>//用于实现栈数据结构
#include <string>//用于处理字符串
#include <sstream>//用于字符串流操作
#include <cctype>//用于字符处理函数
#include <stdexcept>// 用于异常处理

using namespace std;
//用于判断给定的字符是否为运算符。
//如果字符是加号、减号、乘号或除号,则返回 true,否则返回 false。
bool is_operator(char c) {
    return c == '+' || c == '-' || c == '*' || c == '/';
}

//用于确定运算符的优先级。
//对于加号和减号,优先级为 1;对于乘号和除号,优先级为 2。其他字符的优先级为 0。
int precedence(char op) {
    if (op == '+' || op == '-') {
        return 1;
    }
    else if (op == '*' || op == '/') {
        return 2;
    }
    else {
        return 0;
    }
}

//用于应用运算符并返回结果。
//根据传入的运算符,执行相应的加法、减法、乘法或除法操作,并返回结果。如果传入的运算符无效,则抛出运行时错误。
double apply_operator(double a, double b, char op) {
    switch (op) {
    case '+': return a + b;
    case '-': return a - b;
    case '*': return a * b;
    case '/': return a / b;
        //如果某个函数执行失败,可以抛出一个 std::runtime_error 异常对象,并提供相应的错误信息。
    default: throw runtime_error("Invalid operator");
    }
}

//用于计算给定的数学表达式。
//它使用两个栈来存储数字和运算符。
//数字栈用于存储操作数,运算符栈用于存储运算符。
double evaluate_expression(const string& expression) {
    stack<double> num_stack;
    stack<char> op_stack;

    //这个循环遍历整个表达式字符串。对于每个字符,根据其类型执行相应的操作。如果是空格,则跳过;
    //如果是数字或小数点,则解析出完整的数字并将其压入数字栈;
    //如果是运算符,则将其与运算符栈顶的运算符进行比较,并根据优先级决定是否立即应用运算符。
    //如果是左括号,则将其压入运算符栈;
    //如果是右括号,则将匹配的左括号弹出,并将括号内的表达式计算出来。
    //如果遇到无效字符,则抛出运行时错误。
    for (size_t i = 0; i < expression.length(); ++i) {
        //size_t 是一种无符号整数类型,使用 size_t 类型作为循环变量的类型是为了确保能够正确处理表达式的长度,并提高代码的可移植性。
        if (isspace(expression[i])) {
            //isspace(expression[i])用于处理输入的字符串,以便在对字符串进行处理之前先识别和处理其中的空白字符。
            // 执行了对字符串 expression 在索引 i 处的字符进行空白字符判断。
            //如果返回结果为 true,则表示该字符是空白字符;如果返回结果为 false,则表示该字符不是空白字符。
            continue;
        }

        /* 这段代码是一个条件判断和循环的代码块。它的作用是找到一个数字或小数点开始的连续字符序列。
        首先,使用 isdigit(expression[i]) || expression[i] == '.' 判断表达式 expression 在索引 i 处的字符是否为数字或小数点。如果是,则执行以下代码块。
        在代码块中,定义了一个新的变量 j 并将其初始化为 i。然后,使用一个循环来迭代从 j 开始的字符序列。
        在循环的每一次迭代中,首先检查 j 是否超出了字符串 expression 的长度,并且判断 expression[j] 是否是数字或小数点。如果是,就将 j 的值增加 1,继续下一次迭代。
        这个循环会一直持续,直到遇到一个不是数字或小数点的字符,或者到达了字符串 expression 的结尾。在循环结束后,变量 j 将指向字符序列的下一个位置。
        这段代码的目的是找到一个数字或小数点开始的连续字符序列,以便后续处理该数字或小数点。*/
        else if (isdigit(expression[i]) || expression[i] == '.') {
            size_t j = i;
            while (j < expression.length() && (isdigit(expression[j]) || expression[j] == '.')) {
                ++j;
            }

            /*这段代码的作用是将找到的连续数字或小数点字符序列转换为一个双精度浮点数,并将其压入一个名为 num_stack 的栈中。
            首先,通过 expression.substr(i, j - i) 获取从索引 i 到索引 j - 1 的子字符串,该子字符串包含了找到的连续数字或小数点字符序列。
            然后,创建一个 stringstream 对象 ss 并将该子字符串传递给它。stringstream 类提供了一种将字符串转换为其他类型的数据的方法。
            接下来,使用 ss >> number 将 ss 中的字符串转换为一个双精度浮点数,并将其存储在变量 number 中。
            最后,将变量 number 压入名为 num_stack 的栈中,以便后续处理。
            最后一行的 i = j - 1 的目的是将变量 i 更新为 j - 1 的值,以便在循环的下一次迭代中,跳过已经处理过的字符序列。
            总之,这段代码的作用是将找到的连续数字或小数点字符序列转换为双精度浮点数,并将其存储在一个栈中,以便后续处理。*/
            double number;
            stringstream ss(expression.substr(i, j - i));
            ss >> number;
            num_stack.push(number);
            i = j - 1;
        }


        /* 这段代码用于处理表达式中的操作符。
         首先,通过调用 is_operator(expression[i]) 来判断当前字符 expression[i] 是否为操作符。
         如果是操作符,则进入一个循环。循环的条件是操作符栈 op_stack 不为空,并且栈顶操作符的优先级大于或等于当前操作符 expression[i] 的优先级。
         在循环中,首先从操作数栈 num_stack 中弹出栈顶的两个双精度浮点数,分别存储在变量 b 和 a 中。这两个操作数分别代表了运算符左侧和右侧的操作数。
         然后,从操作符栈 op_stack 中弹出栈顶的操作符,并将其存储在变量 op 中。
         接下来,调用 apply_operator(a, b, op) 函数,对操作数 a 和 b 应用操作符 op 进行计算,并将结果压入操作数栈 num_stack 中。
         完成内层循环后,将当前操作符 expression[i] 压入操作符栈 op_stack 中。
         总之,这段代码的作用是处理表达式中的操作符。它会从操作数栈中弹出两个操作数和一个操作符,并进行相应的计算,然后将计算结果压入操作数栈中。
         这个过程会不断重复,直到所有的操作符都被处理完毕。*/
        else if (is_operator(expression[i])) {
            while (!op_stack.empty() && precedence(op_stack.top()) >= precedence(expression[i])) {
                double b = num_stack.top();
                num_stack.pop();
                double a = num_stack.top();
                num_stack.pop();
                char op = op_stack.top();
                op_stack.pop();
                num_stack.push(apply_operator(a, b, op));
            }
            op_stack.push(expression[i]);
        }



        //这段代码处理括号的情况。
        //首先,通过比较 expression[i] 是否等于左括号 '(' 来判断当前字符是否为左括号。
        //如果是左括号,则将其压入操作符栈 op_stack 中。
        //接下来,通过比较 expression[i] 是否等于右括号 ')' 来判断当前字符是否为右括号。
        //如果是右括号,则进入一个循环。循环的条件是操作符栈 op_stack 不为空,并且栈顶的操作符不是左括号 '('。
        //在循环中,首先从操作数栈 num_stack 中弹出栈顶的两个双精度浮点数,分别存储在变量 b 和 a 中。
        //然后,从操作符栈 op_stack 中弹出栈顶的操作符,并将其存储在变量 op 中。
        //接下来,调用 apply_operator(a, b, op) 函数,对操作数 a 和 b 应用操作符 op 进行计算,并将结果压入操作数栈 num_stack 中。
        //完成内层循环后,如果操作符栈 op_stack 为空,或者栈顶的操作符不是左括号 '(',则抛出运行时错误 "Mismatched parentheses",表示括号不匹配。
        //最后,如果操作符栈 op_stack 的栈顶操作符是左括号 '(',则将其弹出。
        //总之,这段代码的作用是处理括号。当遇到左括号时,将其压入操作符栈中;当遇到右括号时,将操作符栈中的操作符逐个弹出并进行计算,直到遇到左括号为止。
         //如果括号不匹配,则抛出运行时错误。如果所有的操作符都处理完毕后,操作符栈应该为空。如果不为空,则表示括号不匹配。最后,将左括号从操作符栈中弹出。
        else if (expression[i] == '(') {
            op_stack.push(expression[i]);
        }
        else if (expression[i] == ')') {
            while (!op_stack.empty() && op_stack.top() != '(') {
                double b = num_stack.top();
                num_stack.pop();
                double a = num_stack.top();
                num_stack.pop();
                char op = op_stack.top();
                op_stack.pop();
                num_stack.push(apply_operator(a, b, op));
            }
            if (op_stack.empty() || op_stack.top() != '(') {
                throw runtime_error("Mismatched parentheses");
            }
            op_stack.pop();
        }
        else {
            throw runtime_error("Invalid character");
        }
    }

    //这个循环处理剩余的运算符,直到运算符栈为空。对于每个运算符,从数字栈中弹出两个操作数,然后应用运算符并将结果压入数字栈。
    while (!op_stack.empty()) {
        double b = num_stack.top();
        num_stack.pop();
        double a = num_stack.top();
        num_stack.pop();
        char op = op_stack.top();
        op_stack.pop();
        num_stack.push(apply_operator(a, b, op));
    }

    //最后,检查数字栈中是否只剩下一个元素。如果不是,则说明表达式无效,抛出运行时错误。否则,返回数字栈中的唯一元素作为计算结果。
    if (num_stack.size() != 1) {
        throw runtime_error("Invalid expression");
    }

    return num_stack.top();
}

//在主函数中,首先提示用户输入一个表达式。然后调用evaluate_expression函数计算表达式的结果,并将结果输出。
//如果在计算过程中发生错误,则捕获并输出错误信息。最后返回0表示程序成功结束。
int main() {
    string expression;
    cout << "Enter an expression: ";
    //getline()函数是C++标准库中的一个字符串输入函数,用于从输入流中读取一行文本并存储到字符串对象中。
    //使用getline()函数可以方便地读取包含空格和其他特殊字符的文本行,它会一直读取输入流直到遇到换行符或文件结束符。
    getline(cin, expression);
    //程序会提示用户输入一行文本,然后使用getline()函数读取输入的文本并存储到expression字符串中,最后输出读取到的文本。

    try {
        double result = evaluate_expression(expression);
        cout << "Result: " << result << endl;
    }
    catch (const runtime_error& e) {
        // // 处理异常的代码块
        cerr << "Error: " << e.what() << endl;
    }

    return 0;
}

希望对你有帮助!加油各位!

相关推荐
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
‘’林花谢了春红‘’4 小时前
C++ list (链表)容器
c++·链表·list
机器视觉知识推荐、就业指导6 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
Swift社区6 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman7 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
IT 青年7 小时前
数据结构 (1)基本概念和术语
数据结构·算法
Yang.997 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
熬夜学编程的小王7 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
zz40_8 小时前
C++自己写类 和 运算符重载函数
c++