227. Basic Calculator II - cocoder39/coco39_LC GitHub Wiki

227. Basic Calculator II

stackless

class Solution:
    def calculate(self, s: str) -> int:
        pre_op = '+'
        pre_num = 0
        cur_num = 0
        res = 0

        for i, ch in enumerate(s):
            if ch.isdigit():
                cur_num = 10 * cur_num + ord(ch) - ord('0')
            if ch in '+-*/' or i == len(s) - 1:
                if pre_op == '+': # pre_num + cur_num ch 
                    res += pre_num
                    pre_num = cur_num
                    pre_op = ch
                    cur_num = 0
                elif pre_op == '-': # pre_num - cur_num ch 
                    res += pre_num
                    pre_num = -cur_num
                    pre_op = ch
                    cur_num = 0
                elif pre_op == '*': # pre_num * cur_num ch 
                    pre_num *= cur_num
                    pre_op = ch
                    cur_num = 0
                elif pre_op == '/': # pre_num / cur_num ch 
                    sign = 1 if pre_num >= 0 else -1
                    pre_num = abs(pre_num) // cur_num * sign
                    pre_op = ch
                    cur_num = 0
        
        # 3 + 5 + 7 +
        # 3 + 5*7 +
        # 3 - 5*7
        return res + pre_num

another stackless approach, use idea from lc 282, easier to understand

class Solution:
    def calculate(self, s: str) -> int:
        cur_num = 0
        res = 0
        pre_operand = 0
        pre_op = '+'
        for i, ch in enumerate(s):
            if ch.isdigit():
                cur_num = 10 * cur_num + ord(ch) - ord('0')
            if ch in '+-*/' or i == len(s) - 1:
                if pre_op == '+': # pre_operand + cur_num ch
                    res += cur_num
                    pre_operand = cur_num
                elif pre_op == '-': # pre_operand - cur_num ch
                    res -= cur_num
                    pre_operand = -cur_num
                elif pre_op == '*': # pre_operand * cur_num ch
                    res -= pre_operand
                    pre_operand *= cur_num
                    res += pre_operand
                elif pre_op == '/': # pre_operand / cur_num ch
                    res -= pre_operand
                    sign = 1 if pre_operand >= 0 else -1
                    pre_operand = abs(pre_operand) // cur_num * sign
                    res += pre_operand
                cur_num = 0
                pre_op = ch
        return res

stack approach

class Solution:
    def calculate(self, s: str) -> int:
        pre_op = '+'
        cur_num = 0
        stack = []

        for i, ch in enumerate(s):
            if ch.isdigit():
                cur_num = 10 * cur_num + ord(ch) - ord('0')
            if ch in '+-*/' or i == len(s) - 1:
                if pre_op == '+': # pre_num + cur_num ch 
                    stack.append(cur_num)
                elif pre_op == '-': # pre_num - cur_num ch 
                    stack.append(-cur_num)
                elif pre_op == '*': # pre_num * cur_num ch 
                    stack[-1] *= cur_num
                elif pre_op == '/': # pre_num / cur_num ch 
                    sign = 1 if stack[-1] >= 0 else -1
                    stack[-1] = abs(stack[-1]) // cur_num * sign
                pre_op = ch
                cur_num = 0
        
        # 3 + 5 + 7 +
        # 3 + 5*7 +
        # 3 - 5*7
        return sum(stack)

Notes 2024

thinking process:

  • how to solve it if there is no * and /
  • with * or /, we need to hold a few variables before applying them pre_num pre_op cur_num cur_op
    • when we see cur_op, we need to use pre_op to decide how to process pre_num
      • if pre_op == + or -, then res += pre_num
      • if pre_op == * or /, then consolidated cur_num into pre_num
      • rolling pre_num and pre_op with cur_num and cur_op accordingly. note that pre_num = -cur_num when pre_op == -
  • by checking cur_op, we will have one last segment unprocessed. we can cover it by checking i == len(s) -1, this is like appending "+" to the end of the input string. now we have pre_num built, but not included in the final result

a few tricks:

  • -3 // 2 = -2 instead of -1. the requirement is that division should truncate toward zero
  • if i == len(s) - 1 or ch in '+-*/': cannot be changed to elif. because we need to handle last digit which should go through both if branches
class Solution:
    def calculate(self, s: str) -> int:
        
        num = 0
        stack = []
        last_op = '+'
        for ch in s:
            if ch.isdigit():
                num = num * 10 + ord(ch) - ord('0')
            elif ch in '+-*/':
                if last_op == '*':
                    stack[-1] *= num
                elif last_op == '/':
                    val = stack[-1]
                    sign = 1 if val >= 0 else -1
                    stack[-1] = abs(val) // num * sign
                elif last_op == '+':
                    stack.append(num)
                elif last_op == '-':
                    stack.append(-num)
                num = 0
                last_op = ch
        
        if last_op == '*':
            stack[-1] *= num
        elif last_op == '/':
            val = stack[-1]
            sign = 1 if val >= 0 else -1
            stack[-1] = abs(val) // num * sign
        elif last_op == '+':
            stack.append(num)
        elif last_op == '-':
            stack.append(-num)
        
        return sum(stack) 

option 2: optimization without using stack (hard)

  • keep track of last num, cur num, last op
  • if last op is +/-, last num is always added to final res, last num gets value from cur num
    • 1 +/- 2 +/-
    • 1 +/- 2 *(or/)
  • if last op is *(or/), res cannot be updated, last num = last num *(or/) cur num
    • 1 (or/) 2 +/-/(or/)
class Solution:
    def calculate(self, s: str) -> int:
        cur_num = 0
        last_num = 0
        res = 0
        last_op = '+'

        for ch in s:
            if ch.isdigit():
                cur_num = cur_num * 10 + ord(ch) - ord('0')
            elif ch in '+-*/': 
                if last_op == '*': # last_num * cur_num ch
                    last_num *= cur_num
                elif last_op == '/': # last_num / cur_num ch
                    sign = 1 if last_num >= 0 else -1
                    last_num = abs(last_num) // cur_num * sign
                elif last_op == '+': # last_num + cur_num ch
                    res += last_num
                    last_num = cur_num
                elif last_op == '-': # last_num - cur_num ch
                    res += last_num
                    last_num = -cur_num
                cur_num = 0
                last_op = ch
        
        if last_op == '*': # last_num * cur_num
            last_num *= cur_num
            res += last_num
        elif last_op == '/': # last_num / cur_num
            sign = 1 if last_num >= 0 else -1
            last_num = abs(last_num) // cur_num * sign
            res += last_num
        elif last_op == '+': # last_num + cur_num
            res += last_num + cur_num
        elif last_op == '-': # last_num - cur_num
            res += last_num - cur_num
        
        return res         

simplify code structure above. but attention to switch elif to if

class Solution:
    def calculate(self, s: str) -> int:
        cur_num = 0
        last_num = 0
        res = 0
        last_op = '+'

        for i, ch in enumerate(s):
            if ch.isdigit():
                cur_num = cur_num * 10 + ord(ch) - ord('0')
            if ch in '+-*/' or i == len(s) - 1: # switch to if so after processing the last digit, it will execute the flow here
                if last_op == '*': # last_num * cur_num ch
                    last_num *= cur_num
                elif last_op == '/': # last_num / cur_num ch
                    sign = 1 if last_num >= 0 else -1
                    last_num = abs(last_num) // cur_num * sign
                elif last_op == '+': # last_num + cur_num ch
                    res += last_num
                    last_num = cur_num
                elif last_op == '-': # last_num - cur_num ch
                    res += last_num
                    last_num = -cur_num
                cur_num = 0
                last_op = ch
        
        return res + last_num   

============================================================================

int calculate(string s) {
        stack<int> stk;
        int i = 0;
        char op = '+';
        while (i < s.length()) {
            //skip whitespaces
            while (i < s.length() && s[i] == ' ')   i++;
            if (i >= s.length())    break;
            
            if (isdigit(s[i])) {
                // extract num
                int num = 0;
                while (i < s.length() && isdigit(s[i])) {
                    num = num * 10 + (s[i++] - '0');
                }
                
                if (op == '*') {
                    int tmp = stk.top();
                    stk.pop();
                    stk.push(tmp * num);
                } else if (op == '/') {
                    int tmp = stk.top();
                    stk.pop();
                    stk.push(tmp / num);
                } else if (op == '+') {
                    stk.push(num);
                } else if (op == '-') {
                    stk.push(-num);
                }
            } else {
                op = s[i++];  
            }
        }
        
        int res = 0;
        while (! stk.empty()) {
            res += stk.top();
            stk.pop();
        }
        return res;
    }
⚠️ **GitHub.com Fallback** ⚠️