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

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

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

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

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

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

(括号法)

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

#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;
}

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

目录
相关文章
|
6月前
|
算法 vr&ar 图形学
☆打卡算法☆LeetCode 224. 基本计算器 算法解析
☆打卡算法☆LeetCode 224. 基本计算器 算法解析
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
30 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
1月前
|
算法
数据结构与算法二:栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式
这篇文章讲解了栈的基本概念及其应用,并详细介绍了中缀表达式转换为后缀表达式的算法和实现步骤。
44 3
|
1月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
46 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
3月前
|
算法 C++
第一周算法设计与分析 F : 模拟计算器
该文章 "第一周算法设计与分析 F : 模拟计算器" 的摘要或讨论。这篇文章介绍了如何设计一个程序来模拟一个基本的计算器,处理包含加、减、乘运算的表达式,并给出了相应的C++代码实现
|
6月前
|
存储 算法
算法题解-基本计算器2
算法题解-基本计算器2
|
算法 前端开发 存储
前端算法-计算器
前端算法-计算器
|
24天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
9天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
10天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。