LC 0224 0227 0772 [H] [M] [H] Basic Calculator I II III - ALawliet/algorithms GitHub Wiki

I: +- and ()
II: +-*/
III: +-*/ and () 

so best to study III because it works for I + II + III

  • num = num*10 + int(s[i]) to handle tenths place
  • for addition/subtraction, just loop and add/subtract
  • for multiplication and division, use a stack to get the previous value
  • for parens () grouped expressions, use recursion while tracking the end pointer

int division should truncate (NOT floor) towards 0: use int() or math.trunc() (// breaks with negative numbers)

>>> -3/2
-1.5
>>> int(-3/2)
-1
>>> -3//2
-2
>>> math.trunc(-3/2)
-1

vocab:

  • operator: +, -, *, /
  • operand: number value being operated on

II

'''
the correct answer of -3/2 is -1 because we want to truncate the negative answer towards 0 (round up) with int(x/y) or math.trunc(x/y)
if it were 3/2 it would be 1 because we want to truncate the postive answer towards 0 (round down)
>>> -3/2
-1.5
>>> -3//2
-2
>>> int(-3/2)
-1
>>> int(3//2)
1
>>> int(-3//2)
-2
>>> math.trunc(-3/2)
-1
'''
ops = {
    '+': lambda x: stack.append( x ),
    '-': lambda x: stack.append( -x ),
    '*': lambda x: stack.append( stack.pop()*x ),
    '/': lambda x: stack.append( int( stack.pop()/x ) ) # math.trunc() also works
}
class Solution:
    def calculate(self, s):
        stack = []
        num = 0
        prev_op = '+'

        for i, x in enumerate(s):
            # print(i, x) # debug

            if x.isdigit():
                num *= 10 # handle tens places as we build out the current number with x
                num += int(x) # add the actual number
                # print(i, x, num) # debug

            elif x in ops: # else doesn't work because there can be ' '
                ops[prev_op](num) # op is actually the previous op and num is already calculated, we just push it on the stack
                # print(stack) # debug
                # reset
                num = 0
                prev_op = x

        # if i == len(s) - 1: # if we're at the end, we still need to add the last op we calculated
        ops[prev_op](num)
                
        return sum(stack)

III - works for all 3

introduce j as the end pointer for a grouped expression

the recursion of the overall function can return either just the answer sum(stack) or the group too sum(stack), j

class Solution:
    def calculate(self, s: str) -> int:
        stack = []
        num, prev_op = 0, '+'
        
        i = 0 # we need to use a while loop here to track and manage i
        while i < len(s):
            # handle the usual
            if s[i].isdigit():
                num = num*10 + int(s[i])

            elif s[i] in ops:
                ops[prev_op](num)
                num, prev_op = 0, s[i]

            # handle groups
            elif s[i] == '(':                                   # for BC I and BC III
                num, j = self.calculate(s[i+1:])
                i += j # the recursive call returns and we move i past the grouped expression: (i+j)
            elif s[i] == ')':                                   # for BC I and BC III
                ops[prev_op](num)
                j = i+1 # j represents the end of the grouped expression after it is parsed because we moved i then add the +1 for ): )
                return sum(stack), j

            i += 1

        ops[prev_op](num) # still need to do last operation
        return sum(stack)

wrap it all in a helper method for recursion (better to stick to iterative, it is easier than this)

class Solution:
    def calculate(self, s):
        s += '$' # extra char to remove n-1 check
        def helper(stack, i):
            num, op = 0, '+'
            while i < len(s): # while loop to control i
                c = s[i]
                if c.isspace():
                    i += 1
                elif c.isdigit():
                    num = 10*num + int(c)
                    i += 1
                elif c == '(':
                    num, i = helper([], i+1) # move past (
                else: # '+-*/' or ')' or i == n-1 
                    ops[op](num)
                    num, op = 0, c
                    i += 1
                    if c == ')':
                        return sum(stack), i # return ans to parent who invoked recursion
            return sum(stack)
        return helper([], 0)