Taro Logo

Evaluate Reverse Polish Notation

Medium
1 views
2 months ago

You are given an array of strings tokens that represents an arithmetic expression in a Reverse Polish Notation. Evaluate the expression. Return an integer that represents the value of the expression. The valid operators are '+', '-', '*', and '/'. Each operand may be an integer or another expression. The division between two integers always truncates toward zero. There will not be any division by zero. The input represents a valid arithmetic expression in a reverse polish notation. The answer and all the intermediate calculations can be represented in a 32-bit integer.

For example:

Input: tokens = ["2","1","+","3","*"] Output: 9 Explanation: ((2 + 1) * 3) = 9

Input: tokens = ["4","13","5","/","+"] Output: 6 Explanation: (4 + (13 / 5)) = 6

How would you approach this problem and what would be the time and space complexity?

Sample Answer
## Evaluating Reverse Polish Notation

This problem requires us to evaluate an arithmetic expression given in Reverse Polish Notation (RPN). RPN, also known as postfix notation, places operators after their operands. We can efficiently solve this using a stack.

### Naive Solution

A naive approach involves iterating through the tokens. If we encounter a number, we push it onto a stack. If we encounter an operator, we pop the top two numbers from the stack, perform the operation, and push the result back onto the stack.  This works because RPN naturally lends itself to stack-based evaluation.

### Optimal Solution

The optimal solution is essentially the same as the naive solution, but with careful consideration of integer division (truncating towards zero) and handling potential errors.

```python
class Solution:
    def evalRPN(self, tokens: list[str]) -> int:
        stack = []
        operators = {
            '+': lambda a, b: a + b,
            '-': lambda a, b: a - b,
            '*': lambda a, b: a * b,
            '/': lambda a, b: int(a / b)
        }

        for token in tokens:
            if token in operators:
                operand2 = stack.pop()
                operand1 = stack.pop()
                result = operators[token](operand1, operand2)
                stack.append(result)
            else:
                stack.append(int(token))

        return stack[0]

Example Usage:

tokens1 = ["2","1","+","3","*"]
print(Solution().evalRPN(tokens1))  # Output: 9

tokens2 = ["4","13","5","/","+"]
print(Solution().evalRPN(tokens2))  # Output: 6

tokens3 = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
print(Solution().evalRPN(tokens3))  # Output: 22

Big(O) Run-time Analysis

The run-time complexity is O(n), where n is the number of tokens in the input array. We iterate through the tokens array once. Each operation (pushing and popping from the stack) takes O(1) time.

Big(O) Space Usage Analysis

The space complexity is O(n) in the worst-case scenario. In the worst case, the stack might contain all the operands before encountering operators. For example, an input like ["1", "2", "3", "4", "+", "+", "+"] would result in the numbers 1, 2, 3, and 4 being on the stack before any operation is performed.

Edge Cases

  1. Empty input: The problem states that the input is a valid RPN expression, so we don't need to explicitly handle an empty input array. However, for robustness, we could add a check at the beginning to return 0 or raise an exception.
  2. Single number input: If the input contains only one number (e.g., ["5"]), the algorithm correctly returns that number.
  3. Division by zero: The problem statement specifies that there will be no division by zero, so we don't need to handle this case explicitly. However, in a more general setting, we'd need to add a check to avoid division by zero.
  4. Invalid tokens: The problem statement assumes valid input. In a real-world scenario, we would need to validate that each token is either a valid operator or an integer.
  5. Integer overflow/underflow: The problem statement mentions that the answer and intermediate calculations can be represented in a 32-bit integer. Python handles integer overflow automatically, so we don't need to worry about it in the Python implementation. In languages like Java or C++, we would need to be mindful of potential overflows and take appropriate action (e.g., using long data types or checking for overflow).