#20 - Valid Parentheses
Valid Parentheses
- Difficulty: Easy
- Topics: String, Stack
- Link: https://leetcode.com/problems/valid-parentheses/
Problem Description
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
An input string is valid if:
- Open brackets must be closed by the same type of brackets.
- Open brackets must be closed in the correct order.
- Every close bracket has a corresponding open bracket of the same type.
Example 1:
Input: s = “()”
Output: true
Example 2:
Input: s = “()[]{}”
Output: true
Example 3:
Input: s = “(]”
Output: false
Example 4:
Input: s = “([])”
Output: true
Constraints:
1 <= s.length <= 104sconsists of parentheses only'()[]{}'.
Solution
1. Problem Deconstruction
Technical Description
Given a string s of length n (1 ≤ n ≤ 10⁴) comprising exclusively the characters ‘(’, ‘)’, ‘[’, ‘]’, ‘{’, ‘}’, determine if s satisfies:
- Type Matching: Every opening bracket ( ‘(’, ‘[’, ‘{’ ) must be closed by a bracket of the identical type.
- Order Preservation: The closing bracket for an opening bracket must appear in the reverse order of opening. Formally, if bracket
bopens after bracketa,bmust close beforea. - Completeness: All closing brackets must have a corresponding open bracket.
Beginner Description
Imagine arranging nested boxes:
- Each opening bracket ( ‘(’, ‘[’, ‘{’ ) starts a new box.
- Each closing bracket ( ‘)’, ‘]’, ‘}’ ) closes the most recently opened box.
- Rules:
- Boxes must be closed with the correct lid (e.g., a round box needs a round lid).
- Inner boxes must be closed before outer boxes.
- No extra lids or missing boxes allowed.
The string is “valid” if all boxes are perfectly closed without leftovers.
Mathematical Description
Let s = c₁c₂...cₙ where cᵢ ∈ Σ = {'(', ')', '[', ']', '{', '}'}.
Define:
- Mapping function
f: Σ → Σwithf('(') = ')', f('[') = ']', f('{') = '}'. - Opening set
O = {'(', '[', '{'}. - Stack
S(initially empty).
Fori = 1ton:- If
cᵢ ∈ O, pushcᵢontoS. - Else, if
Sis non-empty andf(top(S)) = cᵢ, popS. Else, invalid.
String is valid iffS = ∅after processing.
- If
Constraint Analysis
1 ≤ n ≤ 10⁴:- Rules out O(n²) brute-force (e.g., 10⁸ operations worst-case).
- Mandates O(n) or O(n log n) solutions.
- Edge: Single-character strings (e.g.,
"[") are invalid (odd length).
scontains only ‘()[]{}’:- No input sanitation needed.
- Edge: All-opening (e.g.,
"(([") or all-closing (e.g.,"])}") strings.
- Hidden Edges:
- Odd-length strings: Automatically invalid (e.g.,
"()["). - Premature closing: Closing bracket before any opener (e.g.,
"])"). - Interleaved brackets: Valid type count but invalid order (e.g.,
"([)]").
- Odd-length strings: Automatically invalid (e.g.,
2. Intuition Scaffolding
Analogies
- Real-World (Stack of Books):
- Opening bracket: Place a book on a pile.
- Closing bracket: Remove the top book only if its cover matches.
- Invalid if: Wrong cover, removal from empty pile, books left.
- Gaming (RPG Quest Chain):
- Start quest
A(opening bracket). - Start quest
Bbefore completingA. - Must complete
B(closing bracket) beforeA. - Invalid: Complete unstarted quest or leave quests incomplete.
- Start quest
- Math (Function Composition):
- Let
g ∘ fdenote applyingftheng. - Bracketing
(f ∘ g) ∘ his valid;(f ∘ g) ∘ h)is invalid (unmatched)).
- Let
Common Pitfalls
- Ignoring Order:
- Incorrect: Count each bracket type independently. Fails for
"([)]"(counts balanced but order invalid).
- Incorrect: Count each bracket type independently. Fails for
- Ignoring Stack Empty Check:
- Crash for closing without opener (e.g.,
"]").
- Crash for closing without opener (e.g.,
- Assuming Even-Length Suffices:
- Even length doesn’t guarantee validity (e.g.,
"[[))").
- Even length doesn’t guarantee validity (e.g.,
- Incorrect Mapping:
- E.g., Map
')'to'['→ fails"()".
- E.g., Map
- Not Final-Stack Check:
- Returns true for
"("(unmatched opener).
- Returns true for
3. Approach Encyclopedia
1. Brute Force (Recursive Elimination)
Pseudocode:
while any bracket pair exists:
replace "()", "[]", "{}" with ""
return s == ""
Complexity Proof:
- Time: Worst-case O(n²).
- Example:
"(((...)))"(deeply nested). - Each iteration removes one pair (O(n) pairs), scanning O(n) per iteration → O(n²).
- Example:
- Space: O(n) (string storage).
Visualization:
s = "([]){}"
Step 1: Remove "[]" → "(){}"
Step 2: Remove "()" → "{}"
Step 3: Remove "{}" → "" → valid.
2. Stack (Optimal)
Pseudocode:
if n is odd: return False
stack = []
map = {')': '(', ']': '[', '}': '{'}
for c in s:
if c in map.values: stack.push(c)
else:
if stack.empty or map[c] != stack.pop():
return False
return stack.empty
Complexity Proof:
- Time: O(n). Each operation (push/pop) is O(1), traversed once.
- Space: O(n) (stack stores ≤ n/2 elements worst-case).
Visualization:
s = "([])"
Step 1: '(' → stack = ['(']
Step 2: '[' → stack = ['(', '[']
Step 3: ']' → pop '[' → matches ']' → stack = ['(']
Step 4: ')' → pop '(' → matches ')' → stack empty → valid.
4. Code Deep Dive
Optimal Code (Python):
def isValid(s: str) -> bool:
if len(s) % 2 != 0: # Early rejection for odd length
return False
stack = []
mapping = {')': '(', ']': '[', '}': '{'} # Closing → opening map
for char in s:
if char in mapping.values(): # Opening bracket
stack.append(char)
elif char in mapping: # Closing bracket
if not stack or mapping[char] != stack.pop():
return False
return not stack # True if stack empty
Line-by-Line Analysis:
len(s) % 2 != 0:- What: Early check for odd-length strings.
- Why: Odd length guarantees imbalance.
- How: Returns
Falseimmediately.
stack = []:- What: LIFO structure to track openers.
- Why: Enforces “last opened, first closed” rule.
mapping = ...:- What: Dictionary mapping closers to openers.
- Why: Efficiently check type matching.
if char in mapping.values():- What: Detects opening brackets.
- Why: Push to stack for future matching.
elif char in mapping:- What: Handles closing brackets.
- Why: Validate against the last opener.
not stack or mapping[char] != stack.pop():- What: Checks: (a) stack non-empty, (b) top matches closer.
- Why: Prevents popping from empty stack or mismatched brackets.
return not stack:- What: Final stack must be empty.
- Why: Unmatched openers invalidate the string.
Edge Case Handling:
- Example 1 (
"()"):n=2(even) → stack pushes ‘(’ → pops for ‘)’ → stack empty →True.
- Example 3 (
"(]"):- Pushes ‘(’ → encounters ‘]’ →
mapping[']'] = '['≠ stack.pop()=‘(’ →False.
- Pushes ‘(’ → encounters ‘]’ →
- All-Closing (
"]]]"):n=3(odd) → earlyFalse.
- Unmatched Opener (
"("):n=1→ earlyFalse.
5. Complexity War Room
Hardware-Aware Analysis:
- Time: O(n) → 10⁴ operations trivial (<1 ms in Python).
- Space: Worst-case stack size = 5000 elements (n=10⁴).
- Each char: 48 bytes (Python str overhead) → 5000 × 48 B ≈ 240 KB.
- Fits in CPU L3 cache (MBs), negligible for modern systems.
Industry Comparison Table:
| Approach | Time | Space | Readability | Interview Viability |
|---|---|---|---|---|
| Brute Force | O(n²) | O(n) | High | ❌ (Fails large n) |
| Stack (Optimal) | O(n) | O(n) | High | ✅ (Optimal) |
| Stack + Early | O(n) | O(n) | High | ✅ (Best Practice) |
6. Pro Mode Extras
Variants
- Leetcode 678 (Wildcard Parentheses):
scontains ‘*’ treatable as ‘(’, ‘)’ or empty.- Solution: Track min/max possible open counts.
- Leetcode 32 (Longest Valid Parentheses):
- Find longest valid substring.
- Solution: DP or stack with indices.
- Multi-Type with Escapes:
- Support escape characters (e.g.,
"\("is literal). - Solution: State machine with escape flags.
- Support escape characters (e.g.,
Interview Cheat Sheet
- First Steps:
- Check length parity.
- Declare stack and bracket mapping.
- Must-Mention:
- “Stack ensures LIFO order for bracket matching.”
- “Early odd-length check optimizes half the cases.”
- Edge Tests:
"["(unmatched opener),"]"(unmatched closer),"([)]"(interleaved).
- Time/Space: Always state first: “O(n) time, O(n) space.”