题目:

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

示例 1:

输入:s = "1 + 1"
输出:2
示例 2:

输入:s = " 2-1 + 2 "
输出:3
示例 3:

输入:s = "(1+(4+5+2)-3)+(6+8)"
输出:23

提示:

  • 1 <= s.length <= 3 * 105
  • s 由数字、'+'、'-'、'('、')'、和 ' ' 组成
  • s 表示一个有效的表达式
  • '+' 不能用作一元运算(例如, "+1" 和 "+(2 + 3)" 无效)
  • '-' 可以用作一元运算(即 "-1" 和 "-(2 + 3)" 是有效的)
  • 输入中不存在两个连续的操作符
  • 每个数字和运行的计算将适合于一个有符号的 32位 整数

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/basic-calculator
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

这道题对于现在的我还挺困难的参考三叶姐@宫水三叶的题解,梳理一下这道题的思路

利用双栈来解决:

  • 使用栈nums:来存放所有的操作数
  • 使用栈ops:来存放所有的操作符

将s中所有的空格去掉,并转换成字符数组,然后从前往后遍历字符,根据遇到的字符来分情况讨论:

  • 遇到左括号 ( :就将其加入到ops中,等待与之匹配的右括号 );
  • 遇到右括号  ):ops中操作符不为空时,就将栈中已有操作数和操作符进行计算,将计算结果放进nums中,直到遇到与之匹配的最近的左括号停止,弹出左括号;
  • 遇到整数:将当前位置开始的连续整数取出,放入nums中;
  • 有新的操作符入栈时,先将栈中已有同等级的操作符和操作数进行计算,计算结果放入nums,直到操作符为空或者遇到左括号时,再入栈,后续再进行计算剩下的。

注意点:

1.第一个数可能为负数(-6):这样第一个操作符可能被认为 '减号',计算时没有前面的数与之计算,可以再最开始就加入一个0,变成 0-6这样结果也正好为-6;

2.为了防止括号内出现的首个字符为运算符,将 (- 替换成 ( 0-, (+ 替换成 (0+。

代码:

 1 class Solution {
2 public int calculate(String s) {
3 //定义两个栈,一个用来存放操作数,一个用来存放操作符
4 Deque<Integer> nums = new ArrayDeque<>();
5 Deque<Character> ops = new ArrayDeque<>();
6 //为了防止第一个数出现负数,先将操作数栈中加入0
7 nums.addLast(0);
8 //将空格去掉
9 s = s.replaceAll(" ", "");
10 //将s转换成一个字符数组
11 char[] cs = s.toCharArray();
12 int n = cs.length;
13 for(int i = 0; i < n; i++){
14 char c = cs[i];
15 //如果遇到左括号,就将操作符加入栈中
16 if(c == '('){
17 ops.addLast(c);
18 }else if(c == ')'){
19 while(!ops.isEmpty()){
20 char op = ops.peekLast();
21 //如果不是左括号,就将括号内的数和操作符进行计算
22 if(op != '('){
23 calc(nums, ops);
24 }else{
25 //如果是左括号,就将左括号弹出
26 ops.pollLast();
27 break;
28 }
29 }
30 }else{
31 if(isNum(c)){
32 //定义当前整数
33 int num = 0;
34 int j = i;
35 //从i位开始后面的连续数字整体取出,加入到栈nums中
36 while(j < n && isNum(cs[j])){
37 num = num * 10 + (int)(cs[j++] - '0');
38 }
39 nums.addLast(num);
40 i = j - 1;
41 }else{
42 //将(-和(+ 替换成 (0-和(0+
43 if(i > 0 && (cs[i-1] == '(' || cs[i-1] == '+' || cs[i-1] == '-')){
44 nums.addLast(0);
45 }
46 //有新操作符入栈,先把栈内元素算出,直到没有操作符或遇到左括号,再入栈
47 while(!ops.isEmpty() && ops.peekLast() != '('){
48 calc(nums, ops);
49 }
50 ops.addLast(c);
51 }
52 }
53 }
54 //算剩下的
55 while(!ops.isEmpty()){
56 calc(nums, ops);
57 }
58 return nums.peekLast();
59 }
60
61
62 //定义一个方法用于计算
63 void calc(Deque<Integer> nums, Deque<Character> ops){
64 //如果操作数不足以及无操作符,则无法计算
65 if(nums.isEmpty() || nums.size() < 2) return;
66 if(ops.isEmpty()) return;
67 //取出栈顶元素和操作符进行计算
68 int b = nums.pollLast(), a = nums.pollLast();
69 char op = ops.pollLast();
70 nums.addLast(op == '+' ? a+b : a-b);
71 }
72 //判断字符是否为数字
73 boolean isNum(char c){
74 return Character.isDigit(c);
75 }
76 }

一些注解:

 1 if(isNum(c)){
2 //定义当前整数
3 int num = 0;
4 int j = i;
5 //从i位开始后面的连续数字整体取出,加入到栈nums中
6 while(j < n && isNum(cs[j])){
7 num = num * 10 + (int)(cs[j++] - '0');
8 }
9 nums.addLast(num);
10 i = j - 1;
  • num = num * 10 + (int)(cs[j++] - '0'):就像取出像23,569这种超过一位数的整数;
  • i = j - 1:例如 56+,i从0开始(利用另一个变量j这里来替换一下i),当i为2时就已经不是数字了,整个for循环就结束了,for循环中就会i++,i == 3,就会跳过56后面的加号,所以if这里面就会将i回退一个指向6,后面for循环i++就会刚好到加号的位置。
1   //将(-和(+  替换成 (0-和(0+
2 if(i > 0 && (cs[i-1] == '(' || cs[i-1] == '+' || cs[i-1] == '-')){
3 nums.addLast(0);
4 }

为了规避一些 ‘不合法的计算表达式’,例如 (+5-6) 变成 (0+5-6)

小知识:

Deque双端队列这种数据结构很灵活,即可以满足队列的FIFO(先进先出)特性,又可以满足栈的LIFO(后进先出)特性,那么分别作为队列和栈,Deque类常用的方法:

力扣224(java)-基本计算器(困难)的更多相关文章

  1. 力扣算法经典第一题——两数之和(Java两种方式实现)

    一.题目 难度:简单 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数, 并返回它们的数组下标. 你可以假设每种输入只会对应一 ...

  2. 【力扣leetcode】-787. K站中转内最便宜的航班

    题目描述: 有 n 个城市通过一些航班连接.给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei] ,表示该航班都从城市 fromi 开始,以价格 p ...

  3. 力扣算法题—069x的平方根

    实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例 1: 输入: 4 输出: 2 示例 ...

  4. JS数据结构第六篇 --- 二叉树力扣练习题

    1.第226题:翻转二叉树 递归+迭代两种实现方式: /** 反转二叉树 * Definition for a binary tree node. * function TreeNode(val) { ...

  5. 力扣(LeetCode)删除排序链表中的重复元素II 个人题解

    给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 思路和上一题类似(参考 力扣(LeetCode)删除排序链表中的重复元素 个人题解)) 只不过这里需要用到一个前 ...

  6. C++双指针滑动和利用Vector实现无重复字符的最长子串—力扣算法

    题目: 力扣原题链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/ 给定一个字符串, ...

  7. LeetCode 1244. 力扣排行榜

    地址 https://www.acwing.com/solution/LeetCode/content/5765/ 题目描述新一轮的「力扣杯」编程大赛即将启动,为了动态显示参赛者的得分数据,需要设计一 ...

  8. 力扣50题 Pow(x,n)

    本题是力扣网第50题. 实现 pow(x, n) ,即计算 x 的 n 次幂函数. 采用递归和非递归思路python实现. class Solution: #递归思路 def myPow_recurs ...

  9. LeetCode(力扣)——Search in Rotated Sorted Array2 搜索旋转排序数组 python实现

    题目描述: python实现 Search in Rotated Sorted Array2 搜索旋转排序数组   中文: 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0 ...

  10. LeetCode(力扣)——Search in Rotated Sorted Array 搜索旋转排序数组 python实现

    题目描述: python实现 Search in Rotated Sorted Array 搜索旋转排序数组   中文:假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1 ...

随机推荐

  1. WEBRTC回声消除-AECM算法源码解析之参数解析

    一 概述   webrtc 针对回声问题一共开源了3种回声消除算法,分别为aec,aecm,以及aec3,其中aec是最早期的版本,在后续的更新中aec3的出现代替了aec在webrtc 中的地位,而 ...

  2. stm32 boot0硬件接法导致的概率性启动失败问题总结和反思

    概要  问题概要,板子在稳压电源上工作很好,可一旦接了电池,stm32就会出现概率性的无法启动.加上项目比较急,这个问题阻塞一直无法量产.真是非常的要命啊. 思路分析  既然是不同的电源会导致这个问题 ...

  3. 精通 Grails: 测试 Grails 应用程序

    排除 bug,构建可执行文档 Grails 可以轻松确保您的应用程序从始至终都远离 bug.这还有另一个好处,您可以利用测试代码生成一组通常是最新的可执行文档.本月 Grails 专家 Scott D ...

  4. 天天用defineEmits宏函数,竟然不知道编译后是vue2的选项式API?

    前言 我们每天都在使用 defineEmits 宏函数,但是你知道defineEmits 宏函数经过编译后其实就是vue2的选项式API吗?通过回答下面两个问题,我将逐步为你揭秘defineEmits ...

  5. 面试官:Kafka和ES选主有什么区别?

    Kafka 和 ES 都是用来处理大数据的中间件,一个是消息中间件的代表(Kafka),另一个是大数据搜索引擎的代表(ES).它们在 Java 领域的使用非常广泛,在大数据方面就更不用说了,但它们的选 ...

  6. 开发必会系列:《Java多线程编程实战》读书笔记

    如何判断是否开启超线程 一  基础 进程是程序向操作系统申请资源(如内存空间和文件句柄)的基本单位.线程是进程中可独立执行的最小单位. 在Java平台中创建一个线程就是创建一个Thread类(或其子类 ...

  7. [Java]小功能

    [版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://blog.csdn.net/m0_69908381/article/details/130858061 出自[进步* ...

  8. 【Learning eBPF-1】什么是 eBPF?为什么它很吊?

    本书中, eBPF 被称为一种 革命性的 内核技术,被广泛应用于网络.观测 和 安全工具中. 这种技术允许你在不重新编译内核的情况下,使能你的自定义工具,与内核数据进行交互.听起来很厉害. 1.1 追 ...

  9. 【Java面试题】Hibernate

    六.Hibernate 50)简述一下 hibernate 的开发流程 第一步:加载 hibernate 的配置文件,读取配置文件的参数(jdbc 连接参数,数据 库方言,hbm 表与对象关系映射文件 ...

  10. MySQL索引Innodb存储引擎

    MySQL索引优化 一.基础理解 MySQL语句的查询效率主要和索引树的高度有关,想要降低查询的次数提高查询的速度,减少直接对磁盘的I/O流的次数,就要让索引树的高度越低越好. 索引的定义:索引是帮助 ...