Leetcode solution 227: Basic Calculator II

 Problem Statement 

Implement a basic calculator to evaluate a simple expression string.

The expression string contains only non-negative integers, +-*/ operators and empty spaces . The integer division should truncate toward zero.

Example 1:

Input: "3+2*2"
Output: 7

Example 2:

Input: " 3/2 "
Output: 1

Example 3:

Input: " 3+5 / 2 "
Output: 5

Note:

  • You may assume that the given expression is always valid.
  • Do not use the eval built-in library function.

Problem link

 

Video Tutorial

You can find the detailed video tutorial here

Thought Process

This problem is very similar to Basic Calculator. The difference is there is no parenthesis in this one, but there is * and / . We can use similar thought process by having two stacks,  one stack for the operator and the other for the operands. We just need to pay attention the differentiate the operator's priority. For example, when you currently see a "+" or "-"  and previous operator is "*" or "/", you need to pop up the operator stack and calculate. When you currently see a "*" or "/" and previous operator is "+" or "-", you should just keep pushing operator to stack. Else, they are at the same priority, we just do the normal calculation.

A few caveats

  • Notice number overflow. "0- 2147483648". I don't think leetcode has this test case but it is a valid one. We should use Long.
  • Pay attention to the order when popping out operands and calculate, the order matters. 
  • The number might not be just single digit, so need to read the char and convert the numbers

Solutions

 

Standard generic way

Keep two stacks, operator and operands as explained in the above "Thought Process"

 1 public int calculate(String s) {
 2         if (s == null || s.length() == 0) {
 3             throw new IllegalArgumentException("invalid input");
 4         }
 5 
 6         int i = 0;
 7         // even though leetcode does not have this use case System.out.println(ins.calculate("0-2147483648")); // -2147483648
 8         // It can still pass with Integer, but use long for overflow case in general
 9         Stack<Long> operands = new Stack<>();
10         Stack<Character> operators = new Stack<>();
11         StringBuilder number = new StringBuilder(); // deal with non single digit numbers
12 
13         while (i < s.length()) {
14             char c = s.charAt(i);
15             if (c == ' ') {
16                 i++;
17                 continue;
18             }
19 
20             if (Character.isDigit(c)) {
21                 number.append(c);
22             } else if (c == '+' || c == '-' || c == '*' || c == '/') {
23                 if (number.length() != 0) {
24                     operands.push(Long.parseLong(number.toString()));
25                     number = new StringBuilder();
26                 }
27                 // Basically based on different priority of operators
28                 if (operators.isEmpty()) {
29                     operators.push(c);
30                 } else if (!operators.isEmpty() && (c == '*' || c == '/') && (operators.peek() == '+' || operators.peek() == '-')) {
31                     // do nothing, keep pushing because */ has higher priority than +-
32                     operators.push(c);
33                 } else if (!operators.isEmpty() && (c == '+' || c == '-') && (operators.peek() == '*' || operators.peek() == '/')) {
34                     // calculate all previous expressions
35                     while (!operators.isEmpty()) {
36                         operands.push(this.calculateValue(operands, operators.pop()));
37                     }
38                     operators.push(c);
39                 } else {
40                     // only calculating one step, for */, and +- case, one step is fine
41                     operands.push(this.calculateValue(operands, operators.pop()));
42                     operators.push(c);
43                 }
44             }
45             i++;
46         }
47 
48         if (number.length() != 0) {
49             operands.push(Long.parseLong(number.toString()));
50         }
51         // for "3+2*2" case that's why we need a while loop
52         while (!operators.isEmpty()) {
53             operands.push(this.calculateValue(operands, operators.pop()));
54         }
55 
56         return (int)operands.pop().longValue(); // Since it is Long, an object can't be cast to primitive, .longValue first then cast
57     }
58 
59 
60     private long calculateValue(Stack<Long> operands, char op) {
61         long o2 = operands.pop();
62         long o1 = operands.pop();
63 
64         if (op == '+') {
65             return o1 + o2;
66         } else if (op == '-') {
67             return o1 - o2;
68         } else if (op == '*') {
69             return o1 * o2;
70         } else if (op == '/') {
71             return o1 / o2;
72         } else {
73             throw new IllegalArgumentException("invalid op!");
74         }
75     }

Time Complexity: O(N), N is the length of the string

Space Complexity: O(N), extra stack is needed

Use one stack with two passes

Another neat and clean way to solve this problem is also similar to the "Use the sign method with one stack" in Basic Calculator. The idea is in the first pass, only calculate "*" and "/" and push the values into the stack, then have a 2nd pass to do the "+" and "-" calculations.

 1 // Another thought is having 2 pass, first pass */, second pass +-
 2     // "1 + 2 * 3 / 2" = 4, pretty clean
 3     // "1 - 2 * 3 / 2" = -2
 4     public int calculateTwoPass(String s) {
 5         int len;
 6         if (s == null || (len = s.length()) == 0) {
 7             return 0;
 8         }
 9         Stack<Integer> stack = new Stack<Integer>();
10         int num = 0;
11         // This is more like the previous sign
12         char sign = '+';
13         for (int i = 0; i < len; i++) {
14             if (Character.isDigit(s.charAt(i))) {
15                 num = num * 10 + s.charAt(i) - '0';
16             }
17 
18             if ((!Character.isDigit(s.charAt(i)) && ' ' != s.charAt(i)) || i == len - 1) {
19                 if (sign == '-') {
20                     stack.push(-num);
21                 }
22                 if (sign == '+') {
23                     stack.push(num);
24                 }
25                 if (sign == '*') {
26                     stack.push(stack.pop()*num);
27                 }
28                 if (sign == '/') {
29                     stack.push(stack.pop()/num);
30                 }
31                 // reassign the current sign
32                 sign = s.charAt(i);
33                 num = 0;
34             }
35         }
36 
37         int re = 0;
38         for (int i : stack){
39             re += i;
40         }
41         return re;
42     }

Time Complexity: O(N), N is the length of the string

Space Complexity: O(N), extra stack is needed

References

猜你喜欢

转载自www.cnblogs.com/baozitraining/p/11331465.html