使用双栈解决中缀表达式计算
问题 :给你一个字符串表达式 s
,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。
s
一定是一个有效表达式- 表达式中的所有整数都是非负整数,且在范围
[0, 2^31^ - 1]
内s
由整数和算符 (’+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开
实际上还是逆波兰思想,只不过逆波兰将中缀转后缀时没有直接计算,这里计算了。另外本模板由于题目现在没有考虑「负数运算」和「负数符号」,即类型 15/-5 这种情况,需要注意。
对于「任何表达式」,我们都可以使用两个栈 nums
和 ops
:
nums
:存放所有数字ops
: 存放所有数字以外的操作
然后从前往后遍历,将每个字符分情况讨论
-
数字:取出一个完整的数字,放入
nums
-
运算符:放入
ops
,放入前把栈内能算的可以算掉(只有「栈内运算符」比「当前运算符」高or同级别才运算),使用现有的nums
和ops
进行计算,直到没有操作或者遇到左括号,计算结果放到nums
-
(
: 直接加入ops
中,等待与之匹配的)
-
)
: 使用现有的nums
和 ops 进行计算,直到遇到左边最近的一个左括号为止,计算结果放到nums
class Solution{
//1.先用map维护一个优先级字典并写好一个具体计算函数calc()
Map<Character,Integer> map=new HashMap<>(){{
put('-',1); put('+',1);
put('*',2); put('/',2);
put('^',3);
}};
//2.建立两个栈,开始扫描
public int claculate(String s){
// tip:将所有的空格去掉,并将 (- 替换为 (0-,(+ 替换为 (0+
// 当然这里也可以不预处理,而是放到循环里面去做判断
s = s.replaceAll(" ", "");
s = s.replaceAll("\\(-", "(0-");
s = s.replaceAll("\\(\\+", "(0+");
char[]expression=s.toCharArray();
int capacity =s.length;
Deque<Integer>nums=new ArrayDeque<>();
Deque<Character> ops=new ArrayDeque<>();
//防止第一个数为负数,先往nums里添加0
nums.addLast(0);
for(int i=0;i<capacity;i++){
char c=expression[i];
if(c=='('){
ops.addLast(c);
}else if(c==')'){
//碰到右括号,计算到最近一个左括号为止
while(!ops.isEmpty()){
if(ops.peekLast()=='('){
ops.pollLast();
break;
}else{
calc(nums,ops);
}
}
}else{
//如果是数字
if(isNumber(c)){
int temp=0;
int cur=i;
//取出一个完整的数字
while(j<capacity && isNumber(expression[j])){
temp=temp*10+(expression[j++]-'0');
}
nums.addLast(temp);
i=j-1;
}else{
//如果是运算符
// 有一个新操作要入栈时,先把栈内可以算的都算了
// 只有满足「栈内运算符」比「当前运算符」优先级高/同等,才进行运算
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();
}
boolean isNumber(char c){
return Character.isDigit(c);
}
//具体的数学计算规则函数
void calc(Deque<Integer>nums,Deque<Character>ops){
if(nums.isEmpty()||nums.size<2) return;
if(ops.isEmpty()) return;
int second=nums.pollLast(),first=nums.pollLast();
char op =ops.pollLast();
int ans=0;
if (op == '+') ans = first + second;
else if (op == '-') ans = first - second;
else if (op == '*') ans = first * second;
else if (op == '/') ans = first / second;
else if (op == '^') ans = (int)Math.pow(a, b);
nums.addLast(ans);
}
}