表达式求值,支持单位正整数的加、减、乘、除运算,并支持用括号表示优先级别。
表达式一定有效,且只包含正整数,结果也自动转为整数。
先看一道不包含括号的类似的题:
参考LeetCode227. Basic Calculator II
解法:
思路:
1.遍历字符串,当遇到运算符或读到末尾,判断前一个运算符
2.之前为+,入栈数字;之前为-,入栈相反数;之前为*或/,出栈数字计算后再入栈。
3.当前运算符变为前一个运算符,继续遍历。
4.遍历结束,若栈不为空,循环出栈数字求和。
代码:
class Solution {
static Set<Character> set;
static {
set = new HashSet<>();
set.add('+');
set.add('-');
set.add('*');
set.add('/');
}
public static int calculate(String s) {
int num = 0;
Stack<Integer> stack = new Stack<>();
char sign = '+';
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isDigit(c)) {
num = num * 10 + c - '0';
}
if (set.contains(c) || i == s.length() - 1) {
if (sign == '+') {
stack.push(num);
} else if (sign == '-') {
stack.push(-num);
} else if (sign == '*') {
int tmp = stack.pop() * num;
stack.push(tmp);
} else if (sign == '/') {
int tmp = stack.pop() / num;
stack.push(tmp);
}
sign = c;
num = 0;
}
}
int result = 0;
while (!stack.isEmpty()) {
result += stack.pop();
}
return result;
}
}
然后再看包含括号的情况,参考LeetCode224. Basic Calculator。一种直接简单的解法是直接在上一题的基础上修改下,如果遍历到左括号,则记录下左边界,继续遍历直到匹配的右括号,将括号里的子表达式递归求值,然后从新的起点继续遍历。(代码1)
当然,我们很容易发现代码1重复遍历了括号中的子串,当然,我们也可以一次遍历就算出表达式结果。我们使用一个全局变量i标记当前遍历的索引值,当遇到左括号开始递归遍历,遇到右括号返回当前递归的子表达式的值,递归返回后,我们立即处理这个值入栈而不是等到下一个运算符出现,因为可能没有下一个运算符了,要考虑到右括号是最后一个字符这种情况。(见代码2)
代码1:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
public class Eval
{
private static int eval(String s)
{
Stack<Integer> st = new Stack<>();
//sign表示之前的运算符
char sign = '+';
String operator = "+-*/";
int num = 0;
for(int i = 0;i<s.length();i++){
char c = s.charAt(i);
if(Character.isDigit(c)){
num = num * 10 + c - '0';
}
if(operator.indexOf(c) != -1 || i == s.length()-1){
if(sign == '+')
{
st.push(num);
}
else if(sign == '-')
{
st.push(-num);
}
else if(sign == '*')
{
st.push(st.pop() * num);
}
else if(sign == '/')
{
st.push(st.pop() / num);
}
sign = c;
num = 0;
}
if(c == '('){
int cnt = 1;
int j = i;
while(cnt != 0){
j++;
if(s.charAt(j) == '(') cnt++;
else if(s.charAt(j) == ')') cnt--;
}
String sub = s.substring(i+1,j);
num = eval(sub);
i = j-1;
}
}
int res = 0;
while(!st.isEmpty()){
res += st.pop();
}
return res;
}
public static void main(String[] args) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String exp;
while((exp = br.readLine()) != null)
{
int ans = eval(exp);
System.out.println(exp + " = " + ans);
}
}
}
代码2:
class Solution {
static int i=0;
public static int calculate(String s) {
Stack<Integer> st = new Stack();
//sign表示之前的运算符
char sign = '+';
String operator = "+-*/";
int num = 0;
int len = s.length();
for(;i<len;i++){
char c = s.charAt(i);
if(Character.isDigit(c)){
num = num * 10 + c - '0';
}
if(operator.indexOf(c) != -1 || i == s.length()-1 || s.charAt(i+1) == ')'){
pushOperand(st, sign, num);
sign = c;
num = 0;
}
if(c == '('){
i=i+1;
num = calculate(s);
//下面的代码考虑的是,右括号作为结束的情况
pushOperand(st, sign, num);
sign = c;//只要不是运算符就不会重复
num = 0;
}
if(c == ')'){
return getResult(st);
}
}
return getResult(st);
}
private static int getResult(Stack<Integer> st) {
int res = 0;
while (!st.isEmpty()) {
res += st.pop();
}
return res;
}
private static void pushOperand(Stack<Integer> st, char sign, int num) {
if (sign == '+') {
st.push(num);
} else if (sign == '-') {
st.push(-num);
}else if(sign == '*'){
st.push(st.pop() * num);
}else if(sign == '/'){
st.push(st.pop() / num);
}
}
public static void main(String[] args) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String exp;
while((exp = br.readLine()) != null)
{
int ans = eval(exp);
System.out.println(exp + " = " + ans);
}
}
}