227. 基本计算器 II
原题链接:
https://leetcode.cn/problems/basic-calculator-ii/
完成情况:
解题思路:
这段代码是一个用于计算表达式的类。它使用两个栈opers
和nums
来存储运算符和操作数。在calculate
函数中,它遍历输入的字符串表达式s
,根据不同的情况进行处理。具体来说:
- 如果遇到空格,则跳过。
- 如果遇到左括号
(
,将其压入opers
栈中,并检查下一个字符是否为负号-
,若是,则将0压入nums
栈中,并将负号-
压入opers
栈中。 - 如果遇到右括号
)
,则不断计算直到遇到左括号(
。 - 如果遇到数字字符,则将连续的数字字符转换为整数,并压入
nums
栈中。 - 如果遇到运算符,则与栈顶运算符比较优先级,若当前运算符优先级小于等于栈顶运算符,则进行计算,直到栈顶运算符优先级小于当前运算符,然后将当前运算符压入
opers
栈中。 - 最后,当遍历完整个表达式后,不断计算直到
opers
栈为空,并返回nums
栈顶元素作为最终计算结果。
cal
函数用于实际计算操作数和运算符的结果,并将结果压入nums
栈中。
这段代码实现了一个简单的表达式计算器,支持加减乘除取模和幂运算。
参考代码:
_227基本计算器II_单栈直接算
package leetcode板块;
import java.util.ArrayDeque;
import java.util.Deque;
public class _227基本计算器II_单栈直接算 {
/**
* String类型实现双栈
* @param s
* @return
*/
public int calculate(String s) {
//双栈实现,一个栈存储数字,一个栈存储计算式
//然后就是数字栈还需要往后看一个,因为如果是连续的数字,那么说明其实他们同属于一个数值
Deque<Integer> myStack = new ArrayDeque<Integer>();
char preSign = '+';
int num = 0;
int n = s.length();
for(int i = 0;i<n;++i){
if (Character.isDigit(s.charAt(i))){
num = num*10 + s.charAt(i) - '0';
}
if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' '|| i == n-1){
switch (preSign){
case '+':
myStack.push(num);
break;
case '-':
myStack.push(-num);
break;
case '*':
myStack.push(myStack.pop() * num);
break;
case '/':
myStack.push(myStack.pop() / num);
break;
}
preSign = s.charAt(i);
num = 0;
}
}
int res = 0;
while (!myStack.isEmpty()){
res += myStack.pop();
}
return res;
}
}
_227基本计算器II_双栈
package leetcode板块;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
public class _227基本计算器II_双栈 {
//这里的优先级划分按照「数学」进行划分即可
Map<Character,Integer> map = new HashMap<>(){{
put('-',1);
put('+',1);
put('*',2);
put('/',2);
put('%',2);
put('^',3);
}};
/**
*
* @param s
* @return
*/
public int calculate(String s){
/*
实例:
输入:s = " 3+5 / 2 "
输出:5
*/
//先去除掉所有的空格
s = s.replaceAll(" ","");
//将字符串转化成字符数组
char [] chars = s.toCharArray();
int n = s.length();
//构建数字栈
Deque<Integer> nums = new ArrayDeque<Integer>();
//构建操作符栈
Deque<Character> options = new ArrayDeque<>();
//为了防止第一个数是负数,且我们没有在一开始就考虑正负号的问题,所以在最外层
//所以一开始先加一个0
nums.addLast(0);
for (int i = 0;i<n;i++){
char ch = chars[i];
if (ch == '('){ //遇到左括号,必定最高优先添加
options.add(ch);
}else if (ch == ')'){ //遇到右括号,那么就赶紧把栈里面都计算掉
while (!options.isEmpty()){
if (options.peekLast() != '('){
calcNumsAndOptions(nums,options);
}else {
options.pollLast();
break;
}
}
}else {
if (isNumber(ch)){
int u = 0;
int j = i;
//判断是否会有连续数字
while (j < n && isNumber(chars[j])){
u = u*10 + (chars[j++] - '0');
}
nums.addLast(u);
i = j - 1;
}else {
if (i>0 && (chars[i-1] == '(' || chars[i-1] == '+' || chars[i-1] == '-')){
nums.addLast(0);
}
// 有一个新操作要入栈时,先把栈内可以算的都算了
// 只有满足「栈内运算符」比「当前运算符」优先级高/同等,才进行运算
while (!options.isEmpty() && options.peekLast() != '('){
char prev = options.peekLast();
if (map.get(prev) >= map.get(ch)){
calcNumsAndOptions(nums,options);
}else {
break;
}
}
options.addLast(ch);
}
}
}
//将剩余的计算完
while (!options.isEmpty()){
calcNumsAndOptions(nums,options);
}
return nums.peekLast();
}
/**
*
* @param ch
* @return
*/
private boolean isNumber(char ch) {
return Character.isDigit(ch);
}
/**
*
* @param nums
* @param options
*/
private void calcNumsAndOptions(Deque<Integer> nums, Deque<Character> options) {
if (nums.isEmpty() || nums.size() < 2) return;
if (options.isEmpty()) return;
int b = nums.pollLast(),a = nums.pollLast();
char ops = options.pollLast();
int res = 0;
if (ops == '+'){
res = a+b;
}else if (ops == '-'){
res = a-b;
}else if (ops == '*'){
res = a*b;
}else if (ops == '/'){
res = a/b;
}else if (ops == '^'){
res = (int)Math.pow(a,b);
}else if (ops == '%'){
res = a%b;
}
nums.addLast(res);
}
}