Skip to content

Instantly share code, notes, and snippets.

@shixiaoyu
Last active August 19, 2019 04:23
Show Gist options
  • Save shixiaoyu/7eb18212426c42d9826ad3707d61101f to your computer and use it in GitHub Desktop.
Save shixiaoyu/7eb18212426c42d9826ad3707d61101f to your computer and use it in GitHub Desktop.
public int calculate(String s) {
if (s == null || s.length() == 0) {
throw new IllegalArgumentException("invalid input");
}
int i = 0;
Stack<Long> operands = new Stack<>();
Stack<Character> operators = new Stack<>();
StringBuilder number = new StringBuilder(); // deal with non single digit numbers
while (i < s.length()) {
char c = s.charAt(i);
if (c == ' ') {
i++;
continue;
}
if (Character.isDigit(c)) {
number.append(c);
} else if (this.isValidOperator(c)) {
if (number.length() != 0) {
operands.push(Long.parseLong(number.toString()));
number = new StringBuilder();
}
// Basically based on different priority of operators
if (operators.isEmpty()) {
// it says it only contains non-negative integer, but now we have "-1 + 2", "-(2+3)*4"
operators.push(c);
} else if (!operators.isEmpty() && (c == '*' || c == '/') && (operators.peek() == '+' || operators.peek() == '-')) {
// do nothing, keep pushing because */ has higher priority than +-
operators.push(c);
} else if (!operators.isEmpty() && (c == '+' || c == '-') && (operators.peek() == '*' || operators.peek() == '/')) {
// calculate all previous expressions till hit the left bracket
while (!operators.isEmpty() && operators.peek() != '(') {
operands.push(this.calculateValue(operands, operators.pop()));
}
operators.push(c);
} else if (c == '(') {
operators.push(c);
} else if (c == ')') {
if (number.length() != 0) {
operands.push(Long.parseLong(number.toString()));
number = new StringBuilder();
}
while (!operators.isEmpty() && operators.peek() != '(') {
char op = operators.pop();
operands.push(this.calculateValue(operands, op));
}
operators.pop(); // pop out the left (
} else {
// for normal +- with previous +- || */ with previous */ case just calculate 1 step ahead
// but because 15 / 3 / 5 should be 1, but it won't work 3 / 5 = 0, so we have to calculate
// every time we can calculate and get result
if (!operators.isEmpty() && operators.peek() != '(') {
operands.push(this.calculateValue(operands, operators.pop()));
}
operators.push(c);
}
}
i++;
}
if (number.length() != 0) {
operands.push(Long.parseLong(number.toString()));
}
// for "3+2*2" case that's why we need a while loop
while (!operators.isEmpty()) {
operands.push(this.calculateValue(operands, operators.pop()));
}
return (int)operands.pop().longValue();
}
private boolean isValidOperator(char op) {
if (op == '+' || op == '-' || op == '*' || op == '/' || op == '(' || op == ')') {
return true;
}
return false;
}
private long calculateValue(Stack<Long> operands, char op) {
long o2 = operands.pop();
long o1 = operands.pop();
if (op == '+') {
return o1 + o2;
} else if (op == '-') {
return o1 - o2;
} else if (op == '*') {
return o1 * o2;
} else if (op == '/') {
return o1 / o2;
} else {
throw new IllegalArgumentException("invalid op! " + op);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment