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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/272265.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[vue]Echart使用手册

[vue]Echart使用手册 使用环境Echart的使用Echart所有组件和图表类型Echart 使用方法 使用环境 之前是在JQuery阶段使用Echart&#xff0c;直接引入Echart的js文件即可&#xff0c;现在是在vue中使用,不仅仅时echarts包&#xff0c;还需要安装vue-echarts&#xff1a; "…

性能暴增的Rope Crystal版本:红宝石(12.25)

文章目录 &#xff08;零&#xff09;版本介绍&#xff08;一&#xff09;主界面调整&#xff08;二&#xff09;模型与性能&#xff08;三&#xff09;创作纪念日 &#xff08;零&#xff09;版本介绍 &#x1f517; Github仓库。 这次圣诞节更新主要是提升性能&#xff01;&…

JavaScript:DOM节点

JavaScript&#xff1a;DOM节点 DOM节点查找节点父节点查找子节点查找兄弟节点查找 插入节点追加节点克隆节点 删除节点浏览器渲染模式回流重绘 DOM节点 DOM树中的每一个内容都称之为节点&#xff0c;主要包括元素节点&#xff0c;属性节点&#xff0c;文本节点等&#xff0c;本…

EasyCVR无人机推流+人数统计AI算法,助力公共场所人群密度管控

一、背景与需求 在公共场所和大型活动的管理中&#xff0c;人数统计和人群密度控制是非常重要的安全问题。传统的方法可能存在效率低下或准确度不足的情况&#xff0c;无法满足现代社会的需求。TSINGSEE青犀可以利用无人机推流AI人流量统计算法&#xff0c;基于计算机视觉技术…

22000mAh 电池,这款国产新机来了场「续航」震撼

见惯了主流智能手机&#xff0c;是时候上一波离谱新机震撼了。 三防手机这一细分类型&#xff0c;咱们普通用户可能接触得比较少&#xff1b; 但对于极限运动、野外探险爱好者来说&#xff0c;这玩意儿可是关键时候能救命的必备神器。 在真正严苛环境面前&#xff0c;性能啥的…

ES慢查询分析——性能提升6 倍

问题 生产环境频繁报警。查询跨度91天的数据&#xff0c;请求耗时已经来到了30s。报警的阈值为5s。 背景 查询关键词简单&#xff0c;为‘北京’ 单次仅检索两个字段 查询时间跨度为91天&#xff0c;覆盖数据为450亿数据 问题分析 使用profle分析&#xff0c;复现监控报警的…

【DevOps 工具链】搭建 项目管理软件 禅道

文章目录 1、简介2、环境要求3、搭建部署环境3.1. 安装Apache服务3.2. 安装PHP环境&#xff08;以php7.0为例 &#xff09;3.3. 安装MySQL服务 4、搭建禅道4.1、下载解压4.2、 配置4.2.1、 启动4.2.2、自启动4.2.3、确认是否开机启动 5、成功安装 1、简介 禅道是国产开源项目管…

nvprof:CUDA编程性能分析工具

nvprof分析工具使您能够从命令行收集和查看分析数据。nvprof能够收集CPU和GPU上与CUDA相关的活动的时间线&#xff0c;包括内核执行、内存传输、内存集和CUDA API调用以及CUDA内核的事件或度量。评测选项通过命令行选项提供给nvprof。分析结果在收集分析数据后显示在控制台中&a…

PS 修改图片为固定大小和固定内存

1. 改为固定大小 点击图像->图像大小 然后就可以根据你的需求进行更改了 2. 改为固定内存 点击文件->存储或者存储为web 然后就是如下界面&#xff1a; 点击确定之后&#xff0c;就会有如下界面&#xff0c;其中右边有图片内存的最大大小&#xff0c;三角形处可以来回…

详解Junit单元测试@Test及Assert断言(一学就会,通俗易懂版)

定义 快速入门&#xff08;扩展&#xff1a;Assert断言&#xff09; 断言机制 运行该类的所有测试类 Junit常用注解 示例 1. 2. 3. 4.实例方法初始化和释放资源 5.静态方法初始化和释放资源 一样的使用

[SWPUCTF 2021 新生赛]hardrce

[SWPUCTF 2021 新生赛]hardrce wp 参考博客&#xff1a;https://www.cnblogs.com/bkofyZ/p/17644820.html 代码审计 题目的代码如下&#xff1a; <?php header("Content-Type:text/html;charsetutf-8"); error_reporting(0); highlight_file(__FILE__); if(is…

2023年民宿管理系统排名前十的是哪些?哪一个的功能强大?

现在说起去旅游&#xff0c;很多都会选择订民宿&#xff0c;因为民宿装修风格更具个性化和本土气息&#xff0c;适合追求新潮的年轻人&#xff0c;拍照打卡效果很好。另外就是民宿布局和家里比较相似&#xff0c;出游人数比较多的话住着更方便。由于这股风潮的兴起&#xff0c;…

模型性能评估简介

模型评估 混淆矩阵 Positive - 正例Negative (N) - 负例 结果: 预测为正类别 预测为负类别 真实为正类别 True Positive (TP) False Negative (FN) 真实为负类别 False Positive (FP) True Negative (TN)TP - 预测 P, 实际 P, 模型预测正确FP - 预测 P, …

promise的使用和实例方法

前言 异步,是任何编程都无法回避的话题。在promise出现之前,js中也有处理异步的方案,不过还没有专门的api能去处理链式的异步操作。所以,当大量的异步任务逐个执行,就变成了传说中的回调地狱。 function asyncFn(fn1, fn2, fn3) {setTimeout(() > {//处理第一个异步任务fn1…

网工内推 | 技术支持、解决方案工程师,RHCA认证优先,带薪年假

01 天融信 招聘岗位&#xff1a;售后技术支持工程师 职责描述&#xff1a; 1.负责公司运营商态势安全项目系统远程维护与运营支持工作。 2.负责远程对态势平台、数据探针进行日常巡检&#xff0c;及时发现故障问题&#xff0c;并反馈处置。 3.负责远程支撑态势平台的功能考核&…

k8s是什么

生么是k8s&#xff1a; Kubernetes:8个字母省略&#xff0c;就是k8s 自动部署&#xff0c;自动扩展和管理容器化部署的应用程序的一个开源系统、 k8s是负责自动化运维管理多个容器化程序的集群&#xff0c;是一个功能强大的容器编排工具。 分布式和集群化的分布式进行容器管…

Elasticsearch:无需搜索 “Christmas” 即可找到有关圣诞节的书籍

随着假期的临近&#xff0c;我期待着变得舒适&#xff0c;拿起一本新书&#xff0c;享受轻松的时光。 但是使用搜索栏在线发现图书并不像看起来那么容易......大多数零售搜索引擎仅依赖于关键字搜索&#xff0c;当我们确切地知道我们正在寻找什么书名时&#xff0c;这很好&…

渗透测试 | php的webshell绕过方法总结

目录 1.php的异或运算 2.通过获取注释去绕过 3.利用字符的运算符​​​​​​​ 4.通过end函数代替[] 5.通过常量去绕过 6.字符串拼接双美元符 7.通过函数定义绕过 8.通过类定义&#xff0c;然后传参分割 9.多传参方式绕过​​​​​​​ 10.通过get_defined_function…

教你一分钟弄清屏幕SPI接口名称

相关文章 快速入门ESP32——开发环境配置Arduino IDE 快速入门ESP32——开发环境配置PlatformIO IDE 快速入门ESP32—— platformIO添加开源库和自己的开发库 一分钟弄清屏幕SPI接口名称 前言一、屏幕SPI接口名称二、与单片机连接总结 前言 最近&#xff0c;我在捣鼓CD屏幕的SP…

四川云汇优想教育咨询有限公司抖音电商服务的领航者

四川云汇优想教育咨询有限公司&#xff0c;作为一家在电商服务领域有着深厚底蕴的企业&#xff0c;一直以来都以其卓越的服务质量在业界树立了良好的口碑。尤其是在抖音电商服务方面&#xff0c;云汇优想更是凭借其出色的实力和精准的策略&#xff0c;成为了行业的佼佼者。 在抖…