questions D-E

This commit is contained in:
2025-05-25 11:08:40 +01:00
parent e6a22f98f8
commit ecf95bd23d
18 changed files with 4022 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
title: Evaluate Reverse Polish Notation
slug: evaluate-reverse-polish-notation
difficulty: medium
leetcode_id: 150
leetcode_url: https://leetcode.com/problems/evaluate-reverse-polish-notation/
categories:
- arrays
- stack
- math
patterns:
- monotonic-stack
description: |
You are given an array of strings `tokens` that represents an arithmetic expression in a [Reverse Polish Notation](http://en.wikipedia.org/wiki/Reverse_Polish_notation).
Evaluate the expression. Return *an integer that represents the value of the expression*.
**Note** that:
- 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 reverse polish notation.
- The answer and all the intermediate calculations can be represented in a **32-bit** integer.
constraints: |
- `1 <= tokens.length <= 10^4`
- `tokens[i]` is either an operator: `"+"`, `"-"`, `"*"`, or `"/"`, or an integer in the range `[-200, 200]`.
examples:
- input: 'tokens = ["2","1","+","3","*"]'
output: "9"
explanation: "((2 + 1) * 3) = 9"
- input: 'tokens = ["4","13","5","/","+"]'
output: "6"
explanation: "(4 + (13 / 5)) = 6"
- input: 'tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]'
output: "22"
explanation: "((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = 22"
explanation:
intuition: |
Imagine you're a calculator that processes one token at a time, left to right. Unlike standard mathematical notation (called *infix notation*) where operators appear between operands like `3 + 4`, Reverse Polish Notation (RPN) places operators *after* their operands: `3 4 +`.
The beauty of RPN is that it **eliminates the need for parentheses** entirely. The order of operations is determined purely by the position of operators in the sequence. When you encounter an operator, you know it applies to the two most recent numbers you've seen.
Think of it like stacking plates: every time you see a number, you place it on top of a stack. When you see an operator, you take the top two plates off, combine them using that operation, and put the result back on top. At the end, you're left with exactly one plate — the final answer.
This "last in, first out" behavior is exactly what a **stack** data structure provides, making it the perfect tool for evaluating RPN expressions.
approach: |
We solve this using a **Stack-Based Evaluation**:
**Step 1: Initialise a stack**
- Create an empty stack to hold operands (numbers)
- Define a set of valid operators for quick lookup: `{'+', '-', '*', '/'}`
&nbsp;
**Step 2: Process each token left to right**
- If the token is a **number**: convert it to an integer and push onto the stack
- If the token is an **operator**: pop the top two elements, apply the operation, and push the result back
&nbsp;
**Step 3: Handle operator order carefully**
- When popping for an operator, the **first pop is the right operand** and the **second pop is the left operand**
- This matters for non-commutative operations: `a - b` is different from `b - a`
- For division, truncate toward zero (use `int(a / b)` in Python, not `//`)
&nbsp;
**Step 4: Return the final result**
- After processing all tokens, the stack contains exactly one element: the answer
- Return this value
&nbsp;
This approach works because RPN guarantees that when we encounter an operator, the stack always contains at least two operands ready for that operation.
common_pitfalls:
- title: Wrong Operand Order
description: |
The most common bug is getting the operand order backwards for subtraction and division.
When you pop twice from the stack, the **first value popped is the right operand** (it was pushed most recently), and the **second value is the left operand**.
For `["4", "2", "-"]`, the stack has `[4, 2]`. When we see `-`, we pop `2` first (right), then `4` (left), computing `4 - 2 = 2`, not `2 - 4 = -2`.
wrong_approach: "result = first_pop - second_pop"
correct_approach: "result = second_pop - first_pop"
- title: Division Truncation Direction
description: |
The problem states division **truncates toward zero**, not toward negative infinity.
In Python, the `//` operator performs floor division (toward negative infinity), which gives wrong results for negative numbers:
- `-7 // 2 = -4` (floor division, toward -∞)
- `int(-7 / 2) = -3` (truncation toward zero) ✓
Use `int(a / b)` to ensure correct truncation toward zero for all cases.
wrong_approach: "result = left // right"
correct_approach: "result = int(left / right)"
- title: Not Converting Strings to Integers
description: |
The input tokens are **strings**, not integers. Attempting arithmetic on strings will cause errors or unexpected behavior.
Always convert number tokens to integers with `int(token)` before pushing to the stack.
key_takeaways:
- "**Stack for expression evaluation**: Stacks naturally handle nested operations where the most recent operands are needed first"
- "**RPN eliminates ambiguity**: No parentheses needed because operator position determines evaluation order"
- "**Operand order matters**: For non-commutative operations, track which operand is left vs right"
- "**Classic interview pattern**: This problem tests stack fundamentals and appears frequently in coding interviews"
time_complexity: "O(n). We process each token exactly once, and stack operations (push/pop) are O(1)."
space_complexity: "O(n). In the worst case (all operands, then all operators), the stack holds approximately half the tokens."
solutions:
- approach_name: Stack-Based Evaluation
is_optimal: true
code: |
def eval_rpn(tokens: list[str]) -> int:
stack = []
operators = {'+', '-', '*', '/'}
for token in tokens:
if token in operators:
# Pop operands - first pop is RIGHT operand
right = stack.pop()
left = stack.pop()
# Apply the operator
if token == '+':
result = left + right
elif token == '-':
result = left - right
elif token == '*':
result = left * right
else: # token == '/'
# Truncate toward zero, not floor division
result = int(left / right)
stack.append(result)
else:
# It's a number - convert and push
stack.append(int(token))
# Final result is the only element left
return stack[0]
explanation: |
**Time Complexity:** O(n) — Single pass through all tokens.
**Space Complexity:** O(n) — Stack may hold up to n/2 elements.
We iterate through each token once. Numbers get pushed onto the stack; operators pop two values, compute the result, and push it back. The key insight is maintaining correct operand order for subtraction and division, and using truncation toward zero for division.
- approach_name: Using Lambda Functions
is_optimal: true
code: |
def eval_rpn(tokens: list[str]) -> int:
stack = []
# Map operators to their functions
ops = {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'*': lambda a, b: a * b,
'/': lambda a, b: int(a / b) # Truncate toward zero
}
for token in tokens:
if token in ops:
right = stack.pop()
left = stack.pop()
# Apply operator function
stack.append(ops[token](left, right))
else:
stack.append(int(token))
return stack[0]
explanation: |
**Time Complexity:** O(n) — Single pass through all tokens.
**Space Complexity:** O(n) — Stack storage plus constant space for operator map.
This variation uses a dictionary mapping operators to lambda functions, making the code more concise and eliminating the if-elif chain. The logic is identical to the basic approach, just expressed more elegantly using Python's functional features.