Valid Parentheses
LeetCode 20 | Difficulty: Easy | Pattern: Stack Matching Pairs
Problem Statement
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
Examples
Example 1:
Input: s = "()"
Output: trueExample 2:
Input: s = "()[]{}"
Output: trueExample 3:
Input: s = "(]"
Output: falseExample 4:
Input: s = "([)]"
Output: falseExample 5:
Input: s = "{[]}"
Output: trueConstraints
1 <= s.length <= 10^4sconsists of parentheses only'()[]{}'
Why Use a Stack?
A stack is perfect for this problem because of its LIFO (Last In, First Out) property. The most recently opened bracket must be the first one to close.
Why Simple Counting Fails
Consider [(]):
- Count of
[= 1, count of]= 1 - Count of
(= 1, count of)= 1
Counts are balanced, but the string is invalid because brackets are not properly nested.
Stack State Visualization

For input "{[()]}":
Step 1: Read '{'
Stack: ['{']
Step 2: Read '['
Stack: ['{', '[']
Step 3: Read '('
Stack: ['{', '[', '(']
Step 4: Read ')' - matches '('
Stack: ['{', '[']
Step 5: Read ']' - matches '['
Stack: ['{']
Step 6: Read '}' - matches '{'
Stack: []
Result: Stack empty -> VALIDFor input "([)]":
Step 1: Read '('
Stack: ['(']
Step 2: Read '['
Stack: ['(', '[']
Step 3: Read ')' - expects '[' but got ')'
MISMATCH! Return False
Result: INVALIDSolution
def isValid(s: str) -> bool:
"""
Check if the parentheses in string s are valid.
Args:
s: String containing only '(){}[]'
Returns:
True if parentheses are balanced and properly nested
"""
stack = []
# Map closing brackets to their corresponding opening brackets
mapping = {')': '(', '}': '{', ']': '['}
for char in s:
if char in mapping:
# It's a closing bracket
# Check if stack is empty or top doesn't match
if not stack or stack[-1] != mapping[char]:
return False
stack.pop() # Match found, remove the opening bracket
else:
# It's an opening bracket, push to stack
stack.append(char)
# Valid only if all brackets were matched (empty stack)
return len(stack) == 0public boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque<>();
Map<Character, Character> mapping = new HashMap<>();
mapping.put(')', '(');
mapping.put('}', '{');
mapping.put(']', '[');
for (char c : s.toCharArray()) {
if (mapping.containsKey(c)) {
if (stack.isEmpty() || !stack.peek().equals(mapping.get(c))) {
return false;
}
stack.pop();
} else {
stack.push(c);
}
}
return stack.isEmpty();
}Complexity: Time O(n) · Space O(n)
- Time: Single pass through string. Each character involves O(1) operations: dictionary lookup, stack push/pop
- Space: Stack can hold up to n/2 opening brackets. Worst case is all opening brackets like "((((((" where stack grows to n elements
Alternative: Using Set for Opening Brackets
def isValid(s: str) -> bool:
stack = []
mapping = {')': '(', '}': '{', ']': '['}
opening = set('([{')
for char in s:
if char in opening:
stack.append(char)
elif char in mapping:
if not stack or stack.pop() != mapping[char]:
return False
return not stackComplexity: Time O(n) · Space O(n)
- Time: Same as main solution - O(n) single pass. Set lookup for opening brackets is O(1) with 3-element set
- Space: O(n) for stack. The set and mapping are O(1) since they have fixed size (3 bracket types)
Algorithm Walkthrough
Step-by-Step Process
- Initialize an empty stack and a mapping of closing to opening brackets
- Iterate through each character in the string:
- If it's an opening bracket (
(,[,{): push to stack - If it's a closing bracket (
),],}):- If stack is empty: return
False(no matching opener) - If top of stack doesn't match: return
False - If top matches: pop from stack (brackets matched)
- If stack is empty: return
- If it's an opening bracket (
- Return
Trueif stack is empty,Falseotherwise
Complexity Analysis
| Aspect | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Single pass through the string |
| Space | O(n) | Worst case: all opening brackets (e.g., "((((") |
Best/Worst Cases
- Best Case: O(1) space - alternating open/close like
()()() - Worst Case: O(n) space - all opening brackets like
((((((
Edge Cases
# Test cases to consider
test_cases = [
("", True), # Empty string is valid
("(", False), # Single opening bracket
(")", False), # Single closing bracket
("()", True), # Simple pair
("([])", True), # Nested different types
("([)]", False), # Interleaved (invalid)
("((()))", True), # Deep nesting
("(()())", True), # Same level siblings
]Common Variations
1. Valid Parenthesis String (with *)
The * can represent (, ), or empty string.
def checkValidString(s: str) -> bool:
"""LeetCode 678: * can be (, ), or empty."""
lo = hi = 0 # Range of possible open bracket count
for char in s:
if char == '(':
lo += 1
hi += 1
elif char == ')':
lo = max(lo - 1, 0)
hi -= 1
else: # char == '*'
lo = max(lo - 1, 0) # * acts as )
hi += 1 # * acts as (
if hi < 0: # Too many closing brackets
return False
return lo == 0Complexity: Time O(n) · Space O(1)
- Time: Single pass through the string with O(1) operations per character
- Space: Only two integer variables (lo, hi) regardless of input size - no stack needed due to greedy range tracking
2. Minimum Remove to Make Valid
def minRemoveToMakeValid(s: str) -> str:
"""LeetCode 1249: Remove minimum brackets to make valid."""
s = list(s)
stack = [] # Stores indices of '('
for i, char in enumerate(s):
if char == '(':
stack.append(i)
elif char == ')':
if stack:
stack.pop()
else:
s[i] = '' # Mark for removal
# Remove unmatched '('
for i in stack:
s[i] = ''
return ''.join(s)Complexity: Time O(n) · Space O(n)
- Time: Two passes - first to identify mismatches O(n), second to mark unmatched '(' O(k) where k <= n. Final join is O(n)
- Space: O(n) to convert string to list for in-place modification. Stack holds at most n/2 indices of '(' characters
3. Longest Valid Parentheses
def longestValidParentheses(s: str) -> int:
"""LeetCode 32: Find longest valid substring."""
stack = [-1] # Base index
max_len = 0
for i, char in enumerate(s):
if char == '(':
stack.append(i)
else:
stack.pop()
if not stack:
stack.append(i) # New base
else:
max_len = max(max_len, i - stack[-1])
return max_lenComplexity: Time O(n) · Space O(n)
- Time: Single pass through string. Each character involves O(1) stack operations and length calculation
- Space: Stack stores indices; worst case is all '(' characters where stack grows to n+1 elements (including base -1)
Interview Tips
What Interviewers Look For
- Pattern Recognition: Quickly identify this as a stack problem
- Edge Cases: Handle empty string, single character, unbalanced brackets
- Clean Code: Use a mapping for bracket pairs
Common Follow-Up Questions
"What if the string contains other characters?"
- Skip non-bracket characters
"Can you solve it with O(1) space?"
- Only for single bracket type
()using counter
- Only for single bracket type
"How would you handle custom bracket types?"
- Pass the mapping as a parameter
Related Problems
Generate Parentheses (LeetCode 22)
Problem: Given n pairs of parentheses, generate all combinations of well-formed parentheses.
Key Insight: Use backtracking with two counters (open and close). Add '(' if open < n, add ')' if close < open.
Approach: Backtracking - build string character by character, pruning invalid paths early.
Complexity: O(4^n / sqrt(n)) time (Catalan number), O(n) recursion depth
Longest Valid Parentheses (LeetCode 32)
Problem: Given a string containing just '(' and ')', find the length of the longest valid parentheses substring.
Key Insight: Use stack to track indices. Initialize with -1 as base. On ')', pop and calculate length from current stack top.
Approach: Stack stores indices of '(' characters. When matching ')', calculate substring length = current_index - stack_top.
Complexity: O(n) time, O(n) space
Remove Invalid Parentheses (LeetCode 301)
Problem: Remove the minimum number of invalid parentheses to make the input string valid. Return all unique valid strings.
Key Insight: BFS level-by-level removal. First level with valid strings = minimum removals needed.
Approach: BFS explores all strings with k removals before k+1. Stop at first level containing valid strings.
Complexity: O(2^n) time (with pruning), O(n) space
Valid Parenthesis String (LeetCode 678)
Problem: String contains '(', ')', and '*' (wildcard that can be '(', ')', or empty). Determine if valid.
Key Insight: Track range of possible open bracket counts (lo, hi). '*' expands the range.
Approach: Greedy with range tracking. lo = min possible open, hi = max possible open. Valid if lo reaches 0.
Complexity: O(n) time, O(1) space