当前位置:网站首页>【表达式计算】表达式计算问题的通用解法(练习加强版,含总结)
【表达式计算】表达式计算问题的通用解法(练习加强版,含总结)
2022-07-29 13:59:00 【宫水三叶的刷题日记】
题目描述
这是 LeetCode 上的 227. 基本计算器 II ,难度为 中等。
Tag : 「表达式计算」
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。
整数除法仅保留整数部分。
示例 1:
输入:s = "3+2*2"
输出:7
示例 2:
输入:s = " 3/2 "
输出:1
示例 3:
输入:s = " 3+5 / 2 "
输出:5
提示:
s由整数和算符 ('+','-','*','/') 组成,中间由一些空格隔开s表示一个 有效表达式表达式中的所有整数都是非负整数,且在范围 内 题目数据保证答案是一个 32-bit整数
双栈
如果你有看这篇 224. 基本计算器 的话,今天这道题就是道练习题。
帮你巩固 双栈解决「通用表达式」问题的通用解法 。
事实上,我提供这套解决方案不仅仅能解决只有 + - ( )的 [224. 基本计算器] 或者 + - * / [227. 基本计算器 II(本题)] 的表达式问题,还能解决 + - * / ^ % ( ) 的完全表达式问题。
甚至支持自定义运算符,只要在运算优先级上进行维护即可。
对于「表达式计算」这一类问题,你都可以使用这套思路进行解决。我十分建议你加强理解这套处理逻辑。
对于「任何表达式」而言,我们都使用两个栈 nums 和 ops:
nums: 存放所有的数字ops:存放所有的数字以外的操作
然后从前往后做,对遍历到的字符做分情况讨论:
空格 : 跳过 (: 直接加入ops中,等待与之匹配的)): 使用现有的nums和ops进行计算,直到遇到左边最近的一个左括号为止,计算结果放到nums数字 : 从当前位置开始继续往后取,将整一个连续数字整体取出,加入 nums+ - * / ^ %: 需要将操作放入ops中。 在放入之前先把栈内可以算的都算掉(只有「栈内运算符」比「当前运算符」优先级高/同等,才进行运算),使用现有的nums和ops进行计算,直到没有操作或者遇到左括号,计算结果放到nums
我们可以通过 来理解 只有「栈内运算符」比「当前运算符」优先级高/同等,才进行运算 是什么意思:
因为我们是从前往后做的,假设我们当前已经扫描到 2 + 1 了(此时栈内的操作为 + )。
如果后面出现的 + 2或者- 1的话,满足「栈内运算符」比「当前运算符」优先级高/同等,可以将2 + 1算掉,把结果放到nums中;如果后面出现的是 * 2或者/ 1的话,不满足「栈内运算符」比「当前运算符」优先级高/同等,这时候不能计算2 + 1。
一些细节:
由于第一个数可能是负数,为了减少边界判断。一个小技巧是先往 nums添加一个 0为防止 () 内出现的首个字符为运算符,将所有的空格去掉,并将 (-替换为(0-,(+替换为(0+(当然也可以不进行这样的预处理,将这个处理逻辑放到循环里去做)从理论上分析, nums最好存放的是long,而不是int。因为可能存在大数 + 大数 + 大数 + … - 大数 - 大数的表达式导致中间结果溢出,最终答案不溢出的情况
代码:
class Solution {
// 使用 map 维护一个运算符优先级
// 这里的优先级划分按照「数学」进行划分即可
Map<Character, Integer> map = new HashMap<>(){
{
put('-', 1);
put('+', 1);
put('*', 2);
put('/', 2);
put('%', 2);
put('^', 3);
}};
public int calculate(String s) {
// 将所有的空格去掉
s = s.replaceAll(" ", "");
char[] cs = s.toCharArray();
int n = s.length();
// 存放所有的数字
Deque<Integer> nums = new ArrayDeque<>();
// 为了防止第一个数为负数,先往 nums 加个 0
nums.addLast(0);
// 存放所有「非数字以外」的操作
Deque<Character> ops = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
char c = cs[i];
if (c == '(') {
ops.addLast(c);
} else if (c == ')') {
// 计算到最近一个左括号为止
while (!ops.isEmpty()) {
if (ops.peekLast() != '(') {
calc(nums, ops);
} else {
ops.pollLast();
break;
}
}
} else {
if (isNumber(c)) {
int u = 0;
int j = i;
// 将从 i 位置开始后面的连续数字整体取出,加入 nums
while (j < n && isNumber(cs[j])) u = u * 10 + (cs[j++] - '0');
nums.addLast(u);
i = j - 1;
} else {
if (i > 0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {
nums.addLast(0);
}
// 有一个新操作要入栈时,先把栈内可以算的都算了
// 只有满足「栈内运算符」比「当前运算符」优先级高/同等,才进行运算
while (!ops.isEmpty() && ops.peekLast() != '(') {
char prev = ops.peekLast();
if (map.get(prev) >= map.get(c)) {
calc(nums, ops);
} else {
break;
}
}
ops.addLast(c);
}
}
}
// 将剩余的计算完
while (!ops.isEmpty()) calc(nums, ops);
return nums.peekLast();
}
void calc(Deque<Integer> nums, Deque<Character> ops) {
if (nums.isEmpty() || nums.size() < 2) return;
if (ops.isEmpty()) return;
int b = nums.pollLast(), a = nums.pollLast();
char op = ops.pollLast();
int ans = 0;
if (op == '+') ans = a + b;
else if (op == '-') ans = a - b;
else if (op == '*') ans = a * b;
else if (op == '/') ans = a / b;
else if (op == '^') ans = (int)Math.pow(a, b);
else if (op == '%') ans = a % b;
nums.addLast(ans);
}
boolean isNumber(char c) {
return Character.isDigit(c);
}
}
时间复杂度: 空间复杂度:
总结
还记得我在 题解 留的「进阶」内容?
如果 + -基础上,再考虑*和/,需要增加什么考虑?如何维护运算符的优先级?
这个进阶问题就对应了 LeetCode 上的两道题:
227. 基本计算器 II :本题,包含符号 + - * /772. 基本计算器 III :有锁题,包含符号 + - * / ( )
在「问题1」的基础上,如果考虑支持自定义符号,例如 a / func(a, b) * (c + d),需要做出什么调整?
这个进阶问题,在 LeetCode 上也有类似的题目:
770. 基本计算器 IV : 包含自定义函数符号
综上,使用三叶提供的这套「双栈通用解决方案」,可以解决所有的「表达式计算」问题。因为这套「表达式计算」处理逻辑,本质上模拟了人脑的处理逻辑:根据下一位的运算符优先级决定当前运算符是否可以马上计算。
最后
这是我们「刷穿 LeetCode」系列文章的第 No.227 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地
本文由 mdnice 多平台发布
边栏推荐
- plsql连接oracle使用完毕之后关闭问题
- 城市污水处理过程模型预测控制研究综述
- 1124. 骑马修栅栏
- The core principles of electronic games
- 第4章_2——视图的使用
- How to merge the code when there is a code conflict in the collaborative development of multiple people?
- trivy如何从非关系型数据库查询数据
- EA&UML日拱一卒-活动图::CallOperationAction(续)
- gdb调试常用概念整理
- Programmers are a group with a high incidence of occupational diseases. Don’t be naive to think that it’s just as simple as being bald.
猜你喜欢

教育部等五部门联合推荐优质课外资源,腾讯产品青少年模式首发

【pytorch】1.6 tensor 基本运算

国产手机将用户变成它们的广告肉鸡,难怪消费者都买iPhone了

Network connection optimization for instant messaging mobile terminal development

FPGA刷题——跨时钟域传输(FIFO+打拍+握手)

升级 MDK 5.37 后的问题处理: AC6编译选项, printf, 重启失效等

Alibaba CTO Cheng Li: open source is the source of basic software!

少儿编程 电子学会图形化编程等级考试Scratch二级真题解析(选择题)2022年6月

蚂蚁三面滑铁卢!遭分布式截胡,靠这些笔记潜修30天,挺进京东

leetcode linked list topic
随机推荐
Vscode builds ESP32-C3 development environment
即时通讯移动端开发之网络连接优化
Network connection optimization for instant messaging mobile terminal development
kubernetes cks strace etcd
Alibaba CTO Cheng Li: open source is the source of basic software!
面试官:生产环境中 CPU 利用率飙高怎么办?
480-82(59、151)
How to return all prime factors of a number?
国产手机将用户变成它们的广告肉鸡,难怪消费者都买iPhone了
AI全流程开发难题破解之钥
The new technical director, who is in the form of a isXxx Boolean type definition, tomorrow need not come!
Violence recursion to dynamic programming 02 (very clever game of CARDS)
Offensive EA&UML day arch - activity diagram: : Variable Actions (continue)
Gdb debugging common concepts finishing
rosbag data plotting MATLAB
PAT 甲级 A1021 Deepest Root
84.(cesium之家)cesium模型在地形上运动
使用云服务器从0开始搭建云端Jupyter Lab|Notebook
How to Improve Embedded Programming with MISRA
Nine kinds of way, teach you to read the resources files in the directory path