C++篇(13)计算器实现 - 指南

news/2025/11/15 23:02:04/文章来源:https://www.cnblogs.com/tlnshuju/p/19226473

C++篇(13)计算器实现 - 指南

2025-11-15 22:58  tlnshuju  阅读(0)  评论(0)    收藏  举报

1、计算器实现思路

我们日常写的计算表达式都是中缀表达式,也就是运算符在中间,运算数在两边,但是直接读取无法马上进行计算,因为一个计算表达式还涉及运算符优先级的问题。比如:1-2*(3-4)+5 中遇到-和*都无法运算,因为后面还有括号,优先级更高。

所以其中一种实现思路就是把中缀表达式转换为后缀表达式,也就是分析计算表达式的优先级,将运算数放在前面,运算符放到运算数后面,然后我们依次读取后缀表达式,遇到运算符就可以进行计算了。后缀表达式也被称为逆波兰表达式(Reverse Polish Notation,RPN)

2、后缀表达式进行运算

https://leetcode.cn/problems/evaluate-reverse-polish-notation/submissions/671060134

后缀表达式因为已经确定好了优先级,运算方式非常简单,就是遇到运算符时,取前面两个运算数进行运算,因为经过中缀转后缀,优先级已经确立好了。

建立一个栈存储运算数,读取后缀表达式,遇到运算数入栈遇到运算符,出栈顶的两个数据进行运算,运算后将结果作为一个运算数入栈,继续参与下一次运算。读取表达式结束之后,最后栈里面的值就是运算结果。

class Solution
{
public:int evalRPN(vector& tokens){stack st;for(auto& str : tokens){//str为数字if((str != "+") && (str != "-") && (str != "*") && (str != "/")){st.push(stoi(str));}else{//str为操作符int right = st.top();st.pop();int left = st.top();st.pop();switch(str[0]){case '+' :st.push(left + right);break;case '-' :st.push(left - right);break;case '*' :st.push(left * right);break;case '/' :st.push(left / right);break;}}}return st.top();}
};

3、中缀表达式转后缀表达式

依次读取计算表达式中的值,遇到运算数直接输出。

建立一个栈存储运算符,利用栈后进先出的性质,遇到后面的运算符,出栈里面存的前面运算符进行比较,确定优先级。

遇到运算符,如果栈为空 或者 栈不为空并且当前运算符比栈顶运算符优先级高,则当前运算符入栈。因为如果栈里面存储的是前一个运算符,当前运算符比前一个运算符优先级高,说明前一个不能运算,当前运算符也不能运算,因为后面可能还有更高优先级的运算符。

遇到运算符,如果栈不为空并且当前运算符比栈顶运算符优先级低,说明栈顶的运算符可以运算了,则输出栈顶运算符,当前运算符继续走前面遇到运算符的逻辑。

如果遇到(),则把括号的计算表达式当成一个子表达式,跟上面思路类似,进行递归处理子表达式,处理后转换出的后缀表达式加在前面表达式的后面即可。

计算表达式或者()中子表达式结束值,输出栈中所有运算符。

示例1
示例2   ①
示例2   ②
示例2   ③
class Solution
{
public:map operatorPrecedence = {{'+',1}, {'-',1}, {'*',2}, {'/',2}};//中缀转后缀void toRPN(const string& s, size_t& i, vector& v){stack st;while(i < s.size()){if(isdigit(s[i])){// 操作数直接输出string num;while(i < s.size() && isdigit(s[i])){num += s[i];++i;}v.push_back(num);}else if(s[i] == '('){// 子表达式,递归处理即可++i;toRPN(s, i, v);}else if(s[i] == ')'){// 子表达式结束// 输出栈里面的剩余运算符while(!st.empty()){v.push_back(string(1, st.top()));st.pop();}++i;return;}else{// 运算符if(st.empty() || (operatorPrecedence[st.top()] < operatorPrecedence[s[i]])){st.push(s[i]);++i;}else{char op = st.top();st.pop();v.push_back(string(1, op));}}}// 表达式结束// 输出栈里面的剩余运算符while(!st.empty()){v.push_back(string(1, st.top()));st.pop();}}
};

4、计算器的实现

https://leetcode.cn/problems/basic-calculator

有了上面两个部分,计算器OJ的大部分问题就解决了,但是这里还有一些问题需要处理。因为OJ中给的中缀表达式是字符串,且字符串中包含空格,需要去掉空格。

int calculate(string s)
{//去掉空格string news;for(auto& ch : s){if(ch != ' '){news += ch;}}size_t i = 0;vector v;toRPN(news, i, v);return evalRPN(v);
}

那是不是这样就行了呢?提交代码之后,提示我们有测试样例未通过。

可以发现,这里还需要区分负数和减号。如果是减号的话,那么前面一个是操作数;如果是负号的话,前面一个是操作符。我们需要将所有的负数-x 转换为 0-x。

int calculate(string s)
{//去掉空格string news;for(auto& ch : s){if(ch != ' '){news += ch;}}//处理负数news.swap(s);news.clear();for(size_t i = 0;i < s.size();i++){if(s[i] == '-' && !isdigit(s[i - 1])){news += "0-";}else{news += s[i];}}size_t i = 0;vector v;toRPN(news, i, v);return evalRPN(v);
}

提交之后发现仍然有测试样例不通过。

说明我们判断条件是不够的,还需要判断减号前面是不是右括号。

int calculate(string s)
{//去掉空格string news;for(auto& ch : s){if(ch != ' '){news += ch;}}//处理负数news.swap(s);news.clear();for(size_t i = 0;i < s.size();i++){if(s[i] == '-' && (!isdigit(s[i - 1]) && s[i - 1] != ')')){news += "0-";}else{news += s[i];}}size_t i = 0;vector v;toRPN(news, i, v);return evalRPN(v);
}

再提交一下。。。

又有测试样例崩溃了,因为这里是s[0]的位置出现-号,因此s[i-1]就会越界。所以这个地方还要加一个条件。

完整题解代码如下:

class Solution {
public:int evalRPN(vector& tokens){stack st;for(int i = 0;i < tokens.size();i++){string& str = tokens[i];if((str != "+") && (str != "-") && (str != "*") && (str != "/")){st.push(stoi(str));}else{int right = st.top();st.pop();int left = st.top();st.pop();switch(str[0]){case '+':st.push(left + right);break;case '-':st.push(left - right);break;case '*':st.push(left * right);break;case '/':st.push(left / right);break;}}}return st.top();}map operatorPrecedence = { {'+',1},{'-',1},{'*',2},{'/',2} };//中缀转后缀void toRPN(const string& s, size_t& i, vector& v){stack st;while (i < s.size()){//操作数直接输出if (isdigit(s[i])){string num;while (i < s.size() && isdigit(s[i])){num += s[i];i++;}v.push_back(num);}else if (s[i] == '('){//子表达式,递归处理即可i++;toRPN(s, i, v);}else if (s[i] == ')'){//子表达式结束//输出栈里面的剩余运算符while (!st.empty()){v.push_back(string(1, st.top()));st.pop();}i++;return;}else{//运算符if (st.empty() || operatorPrecedence[s[i]] > operatorPrecedence[st.top()]){st.push(s[i]);i++;}else{char op = st.top();st.pop();v.push_back(string(1, op));}}}//表达式结束//输出栈里面的剩余运算符while (!st.empty()){v.push_back(string(1, st.top()));st.pop();}}int calculate(string s){//去掉空格string news;for(auto& ch : s){if(ch != ' '){news += ch;}}//处理负数news.swap(s);news.clear();for(size_t i = 0;i < s.size();i++){if(s[i] == '-' && (i == 0 || (!isdigit(s[i - 1]) && s[i - 1] != ')'))){news += "0-";}else{news += s[i];}}size_t i = 0;vector v;toRPN(news, i, v);return evalRPN(v);}
};

5、其他实现思路

https://leetcode.cn/problems/basic-calculator/solutions/646369/ji-ben-ji-suan-qi-by-leetcode-solution-jvir

其实一句话就可以解析:就是去括号的思路,比如1-(1+2)=1-1-2,有乘法和除法类似,比考虑中缀转后缀的思路简单。

class Solution {
public:int calculate(string s){stack ops;ops.push(1);int sign = 1;int ret = 0;int n = s.length();int i = 0;while (i < n){if (s[i] == ' '){i++;}else if (s[i] == '+'){sign = ops.top();i++;}else if (s[i] == '-'){sign = -ops.top();i++;}else if (s[i] == '('){ops.push(sign);i++;}else if (s[i] == ')'){ops.pop();i++;}else{long num = 0;while (i < n && s[i] >= '0' && s[i] <= '9'){num = num * 10 + s[i] - '0';i++;}ret += sign * num;}}return ret;}
};

https://leetcode.cn/problems/basic-calculator-ii

https://leetcode.cn/problems/basic-calculator-ii/solutions/648647/ji-ben-ji-suan-qi-ii-by-leetcode-solutio-cm28

class Solution {
public:int calculate(string s){vector stk;char preSign = '+';int num = 0;int n = s.length();for (int i = 0; i < n; ++i){if (isdigit(s[i])){num = num * 10 + int(s[i] - '0');}if (!isdigit(s[i]) && s[i] != ' ' || i == n - 1){switch (preSign){case '+':stk.push_back(num);break;case '-':stk.push_back(-num);break;case '*':stk.back() *= num;break;default:stk.back() /= num;}preSign = s[i];num = 0;}}return accumulate(stk.begin(), stk.end(), 0);}
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20232306 2025-2026-1 《网络与系统攻防技术》实验五实验报告

1.实验要求 (1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取如下信息: DNS注册人及联系方式 该域名对应IP地址 IP地址注册人及联系方式 IP地址所在国家、城市和具体地理位置 PS:使…

ABC432E sol

eazy ds problem. 题意 给你一个序列 $a$,需要支持单点加 & 全局求 $\max\left(l,\min(r,a_i)\right)$(也就是对于每个 $a_i$,当 $a_i<l$,造成 $l$ 的贡献;当 $a_i \ge r$ 时,造成 $r$ 的贡献;否则造成 …

完整教程:linux离线环境局域网远程ssh连接vscode

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025 CSP-S 趋势记

“去年 CSP-S 就没过初赛,今年呢?” 2025 CSP-S 寄。 T1 60min才发现可以反悔贪心,跑完大样例觉得是对的然后有个等号少判了就 100 -> 65。 T2 连正解都没想出来,只会 \(O(2^k \times (m + nk) + (m + nk) \log…

鲜花:记梦4

“你考虑得怎么样了?” 对面的声音打断了我的思考。我抬头一看,居然是,她。 我竭力克制住我的情绪变化,把悸动深深藏在了心底。 她递给我一个信封:“如果考虑好了的话,就在里面的合同上签字吧。”我连忙伸手去接…

第26天(简单题中等题 二分查找、贪心算法)

打卡第二十六天 1道简单题+2道中等题题目:思路: 贪心+二分查找,维护一个"潜在的最优递增序列集合",让每个长度的递增子序列的末尾元素尽可能小,从而为后续元素提供更多的增长可能性。代码: class Solution…

invalid literal for int() with base 10: abc中的base 10是什么意思? 另外它是怎么知道abc的?

在 Python 错误信息 invalid literal for int() with base 10: abc 中,涉及两个关键问题:base 10 的含义,以及程序如何知道错误输入是 abc。下面分别详细解释: 一、base 10 是什么意思? base 在这里表示“进制”,…

byd秘钥 - MKT

byd秘钥 https://rcorex.github.io/nuttyb-config/

NSubstitute之Substitute.ForT

_currentUser = Substitute.For<ICurrentUser>(); 这是 NSubstitute(一个 .NET 单元测试 mock 框架)中的标准用法,用来“创建一个假的 ICurrentUser 实例”。 下面详细解释它的逻辑。✅ 到底发生了什么? Sub…

DAY1 JAVA PreLearning

day1 markdown leaning font 1.set the title: # title---first class subject,## title---second class subject and so on 2.set the text to bold: text 3.set the text to italic: text 4.perform like deleting …

动态规划实践:数字三角形问题分析

动态规划实践:数字三角形问题分析数字三角形的动态规划分析 按照动态规划的求解步骤,我们一步步拆解这个问题: 1.1 最优子结构与递推方程式 首先明确状态定义:设 dp[i][j]表示从数字三角形顶部(第0行第0列)走到第…

【服务器】服务器被攻击植入了挖矿病毒,CPU一直占用100%,@monthly /root/.cfg/./dealer病毒清除 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

第4章 AI项目管理新范式:从交付功能到交付价值

第4章 AI项目管理新范式:从交付功能到交付价值前文系统剖析了传统项目管理范式在AI场景下的局限性。面对这些系统性挑战,简单的“修补”已无济于事,我们必须进行管理范式的“重构”。本章正式提出AI项目管理的新范式…

牛客101:链表 - 教程

牛客101:链表 - 教程2025-11-15 22:39 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-…

C++ QT_数据转换(数值、QString、QByteArray、结构体)

目录1. 基础类型互转数值 ↔ QStringbool ↔ QStringQString 与 QByteArray 互转结构体与 QByteArray 互转 1. 基础类型互转 数值 ↔ QString//int → QStringint intVal = 255; QString strInt1 = QString::number(i…

LNCPC 2025 游寄

LNCPC 2025 游记热身赛 没去,因为期中考试 TAT. jsh 和 sr 替我报了道,然后我在火车上胡做法。 C 是显然 greedy,D 是一个不知道什么东西。 然后铸币蒟蒻凭借惊人的注意力想到了在二进制上转换做数位 dp,但是时间不…

第3章 传统项目管理在AI中的局限

第3章 传统项目管理在AI中的局限第3章 传统项目管理在AI中的局限本章简介在深刻理解了AI项目的核心特征后,本章将系统性地审视传统项目管理框架在面对这些特征时所暴露出的不适应性。目的在于并非全盘否定经典方法——…

Python 异常处理全面详解(附丰富实例)

Python 异常处理全面详解(附丰富实例) 异常是程序运行时出现的意外错误(如除数为零、文件不存在等),若不处理会导致程序崩溃。Python 提供了完善的异常处理机制,允许开发者捕获并处理这些错误,保证程序的健壮性…

IServiceCollection和IServiceProvider

ServiceCollection 和 ServiceProvider 是 .NET 依赖注入系统中的两个核心概念,但它们的职责完全不同。理解这两个概念对看懂 ASP.NET Core / ABP 框架的 DI 机制非常重要。 下面用非常清晰、分层方式给你解释:✔️ …