Uses a stack to match opening and closing brackets in order
Push on opener, check & pop on closer — fail fast on mismatch
Must check stack is empty at the end — not optional
O(n) time, O(n) space worst-case
The empty string is balanced — don't special-case it
Biggest mistake: forgetting final isEmpty() check or using counter for mixed brackets
Plain-English First
Imagine you're reading a book and every time you open a bracket '(' you put a coin in a cup, and every time you close one ')' you take a coin out. If the cup is empty at the end and you never tried to take a coin from an empty cup, the brackets are balanced. That's literally the entire algorithm. The 'cup' is what programmers call a stack — a structure that remembers what you pushed in and hands it back in reverse order.
Every compiler, code editor, and browser in the world checks whether your brackets match up before it does anything else. When VS Code underlines a missing closing brace in red, or when Java throws a compilation error about unexpected tokens, it's running a version of the balanced parentheses check under the hood. This is not a toy problem — it's the gatekeeper that runs before your code ever executes.
The problem itself is simple to state: given a string of brackets like '({[]})' or '([)]', decide whether every opening bracket has a matching closing bracket in the right order. The tricky part is 'in the right order' — because '([)]' has the same number of openers and closers, yet it's wrong. Order matters, and that's exactly what a stack was designed to track.
By the end of this article you'll understand what a stack is and why it's the perfect tool here, you'll be able to write a complete Java solution from memory, you'll know the two mistakes that trip up nearly every beginner, and you'll be ready to answer the three versions of this question that interviewers love to ask.
How Balanced Parentheses Works — Step by Step
The stack-based algorithm processes the string character by character:
Check '({[}])': 1. push '(', push '{', push '['. 2. See '}': pop '[' — '[' does not match '}' → return False immediately.
Time complexity: O(n) — each character is processed once, push/pop are O(1). Space: O(n) for the stack in the worst case (all opening brackets).
Production Insight
Tracing manually exposes the exact point of failure. In production debugging, add log statements around push and pop to capture the stack state on each step.
The moment a mismatch occurs, you know exactly which bracket and which position caused it.
Use this trace technique when diagnosing broken linters or parsers.
Key Takeaway
Trace '({[}])' and see the mismatch at step 3: '[' vs '}'.
That immediate failure is what makes the stack approach O(n) and reliable.
No other data structure catches ordering errors this fast.
How Balanced Parentheses Works — Algorithm
Use a stack to check if opening brackets are matched and closed in the correct order.
Algorithm: 1. Create an empty stack and a matching map: ')':'(', '}':'{', ']':'['. 2. For each character c in the string: a. If c is an opening bracket ('(', '{', '['): push c. b. If c is a closing bracket: if stack is empty or stack.top != matching[c]: return False. Else pop. 3. Return True only if stack is empty at the end.
Worked example — '([)]' (wrong order): '(': push. '[': push. ')': top='[', does NOT match '('. Return False.
Production Insight
Using a map for matching pairs makes the code cleaner and less error-prone than a chain of if-else. In production, a map also makes it easy to extend to other bracket types (e.g., '<' and '>') without touching logic.
But be careful: if the map is immutable and constructed once, it avoids accidental mutation in concurrent environments.
Always guard the pop with an isEmpty check — an EmptyStackException in production is a silent crash.
Key Takeaway
A matching map simplifies validation and scales to new bracket types.
The stack's top must match the expected closer — use the map, not nested ifs.
And always check isEmpty before pop — it's not optional.
What Is a Stack and Why Does It Fit This Problem Perfectly?
A stack is a collection with one rule: the last thing you put in is the first thing you get out. Programmers call this LIFO — Last In, First Out. Think of a stack of plates at a buffet. You add a clean plate to the top, and when someone takes a plate, they always grab from the top. Nobody digs from the bottom.
A stack supports three core operations. Push adds an item to the top. Pop removes and returns the item from the top. Peek looks at the top item without removing it. That's it. Simple, but powerful.
Now think about why this fits bracket matching perfectly. When you see an opening bracket like '(' or '{' or '[', you don't know yet what will close it — so you push it onto the stack and move on. When you later see a closing bracket like ')' or '}' or ']', the most recently opened bracket — sitting right on top of the stack — is the one that should match it. If it does match, pop it off and keep going. If it doesn't match, or the stack is empty when you expected something there, the string is unbalanced.
The stack's LIFO nature mirrors the natural nesting of brackets. The most recently opened bracket must be the first one closed. That's not a coincidence — stacks exist precisely to handle this kind of nested, ordered structure.
StackDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package io.thecodeforge;
import java.util.Stack;
publicclassStackDemo {
publicstaticvoidmain(String[] args) {
// Java's built-in Stack class — we'll use this throughout the articleStack<Character> bracketStack = newStack<>();
// PUSH: add items to the top of the stack
bracketStack.push('('); // stack is now: ['(']
bracketStack.push('['); // stack is now: ['(', '[']
bracketStack.push('{'); // stack is now: ['(', '[', '{']System.out.println("Stack after pushing three brackets: " + bracketStack);
System.out.println("Top of stack (peek): " + bracketStack.peek()); // looks without removing// POP: remove and return the top item
char topBracket = bracketStack.pop(); // removes '{' — the last thing we pushedSystem.out.println("Popped: " + topBracket); // Output: {System.out.println("Stack after one pop: " + bracketStack); // ['(', '[']// isEmpty: crucial check before popping to avoid EmptyStackExceptionSystem.out.println("Is stack empty? " + bracketStack.isEmpty()); // false
}
}
Output
Stack after pushing three brackets: [(, [, {]
Top of stack (peek): {
Popped: {
Stack after one pop: [(, []
Is stack empty: false
Pro Tip:
Always call isEmpty() before calling pop() or peek(). If you call pop() on an empty stack, Java throws an EmptyStackException at runtime. In the balanced parentheses algorithm, an empty stack when you see a closing bracket means the string is immediately unbalanced — that's your signal to return false right away.
Production Insight
In production systems, stack usage for bracket validation is found in compilers, linters, and code formatters. The most common bug is forgetting isEmpty() before pop.
This leads to EmptyStackExceptions that crash the entire parsing pipeline.
Rule of thumb: every pop() must be preceded by an isEmpty() check, or at least be inside a try-catch that handles the exception gracefully.
Key Takeaway
Stack's LIFO is not a coincidence — it's the exact model for nested structures.
Push openers, pop and match closers.
Always guard pop with isEmpty() — an empty stack on closer means invalid input.
The Algorithm: Walking Through the Logic Step by Step
Here's the core insight stated plainly: scan the string character by character. If the character is an opening bracket ('(', '[', or '{'), push it onto the stack. If it's a closing bracket (')', ']', or '}'), check whether the top of the stack holds the matching opener. If yes, pop and continue. If no — or if the stack is empty — the string is unbalanced, stop and return false.
When you finish scanning every character, check whether the stack is empty. An empty stack means every opener found its closer. A non-empty stack means there are openers left hanging with no closers — also unbalanced.
Let's trace through '({[]})' manually before writing any code. - Read '(' → push. Stack: ['('] - Read '{' → push. Stack: ['(', '{'] - Read '[' → push. Stack: ['(', '{', '['] - Read ']' → top is '[', which matches ']'. Pop. Stack: ['(', '{'] - Read '}' → top is '{', which matches '}'. Pop. Stack: ['('] - Read ')' → top is '(', which matches ')'. Pop. Stack: [] - End of string, stack is empty → BALANCED.
Now trace '([)]'
Read '(' → push. Stack: ['(']
Read '[' → push. Stack: ['(', '[']
Read ')' → top is '[', which does NOT match ')'. → UNBALANCED. Stop immediately.
The mismatch detection happens in the middle of the string. That's what makes this algorithm elegant — it fails fast.
BalancedParenthesesChecker.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package io.thecodeforge;
import java.util.Stack;
publicclassBalancedParenthesesChecker {
/**
* Returnstrueif every opening bracket in the input string
* has a correctly ordered, matching closing bracket.
*/
publicstaticbooleanisBalanced(String inputString) {
// Our stack holds opening brackets as we encounter themStack<Character> openingBrackets = newStack<>();
// Walk through every character in the string one at a timefor (int i = 0; i < inputString.length(); i++) {
char currentChar = inputString.charAt(i);
// --- CASE 1: It's an opening bracket → push and move on ---if (currentChar == '(' || currentChar == '[' || currentChar == '{') {
openingBrackets.push(currentChar);
// --- CASE 2: It's a closing bracket → check if it matches the top ---
} elseif (currentChar == ')' || currentChar == ']' || currentChar == '}') {
// If the stack is empty, there's no opener to match this closerif (openingBrackets.isEmpty()) {
return false; // e.g. input starts with ')' — immediately wrong
}
// Look at the most recently pushed openerchar mostRecentOpener = openingBrackets.pop();
// Check whether the opener and closer are a valid pairboolean isMatchingPair =
(mostRecentOpener == '(' && currentChar == ')') ||
(mostRecentOpener == '[' && currentChar == ']') ||
(mostRecentOpener == '{' && currentChar == '}');
if (!isMatchingPair) {
return false; // e.g. '([)]' — opener and closer don't match
}
// If they matched, the pop already removed the opener — carry on
}
// Any other character (letters, digits, spaces) is ignored
}
// After scanning everything, the stack must be empty.// If it's not, some openers never found their closers.return openingBrackets.isEmpty();
}
publicstaticvoidmain(String[] args) {
String[] testCases = {
"({[]})", // all three bracket types, properly nested
"(())", // nested parentheses
"([)]", // wrong order — should fail
"(((", // openers with no closers — should fail
")))", // closers with no openers — should fail
"", // empty string — technically balanced
"{[()()]}", // complex valid nesting
"({)}", // looks close but order is wrong
};
for (String testCase : testCases) {
String result = isBalanced(testCase) ? "BALANCED" : "UNBALANCED";
// Padding the test case for clean console outputSystem.out.printf("%-15s → %s%n",
testCase.isEmpty() ? "(empty)" : testCase,
result);
}
}
}
Output
({[]}) → BALANCED
(()) → BALANCED
([)] → UNBALANCED
((( → UNBALANCED
))) → UNBALANCED
(empty) → BALANCED
{[()()]} → BALANCED
({)} → UNBALANCED
Interview Gold:
Interviewers often ask 'what's the time complexity?' The answer is O(n) — you visit each character exactly once. Space complexity is O(n) in the worst case because you might push every character onto the stack (imagine a string of all opening brackets). Mention both without being asked and you'll stand out.
Production Insight
This implementation is production-ready for most use cases. But watch out for very long input strings: if the string is 10 million characters of all openers, the stack will consume ~80 MB (assuming a char per 2 bytes + stack overhead). That's fine on most servers, but in memory-constrained environments (e.g., Android, embedded), consider a counter-only approach if the input uses only one bracket type.
Also, the current implementation ignores non-bracket characters. That's correct for most linters, but if you need to parse a full expression (e.g., in a calculator), you'll need to track position and possibly report the exact index of the first mismatch. We'll cover that in an interview variation.
Key Takeaway
The code is simple: push on opener, pop and match on closer, check empty at end.
Fail fast on empty stack or mismatch.
Test with the exact edge cases: '([)]', '(((', ')))', and empty string.
Common Gotchas That Trip Up Nearly Every Beginner
Even after understanding the algorithm, there are two specific bugs that show up in beginner solutions so frequently they're almost a rite of passage. Let's look at them directly, see the broken code, and then see the fix.
The first gotcha is forgetting the final isEmpty() check. Many beginners return true as soon as the loop finishes without errors, not realising that unmatched openers sitting in the stack also make the string invalid.
The second gotcha is popping without checking whether the stack is empty first. If your input string starts with a closing bracket, the stack is empty when you try to pop — and Java throws an EmptyStackException that crashes your program instead of gracefully returning false.
There's also a subtler third mistake: using == to compare String objects instead of char values. This only applies if you accidentally store brackets as Strings rather than chars, but it produces silent wrong answers — one of the nastiest kinds of bugs.
The runnable examples below show each broken scenario and the one-line fix for each.
CommonMistakesDemo.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package io.thecodeforge;
import java.util.Stack;
publicclassCommonMistakesDemo {
// ❌ BUGGY VERSION 1: Missing the final isEmpty() checkpublicstaticbooleanbuggyNoEmptyCheck(String input) {
Stack<Character> stack = newStack<>();
for (char ch : input.toCharArray()) {
if (ch == '(' || ch == '[' || ch == '{') {
stack.push(ch);
} elseif (ch == ')' || ch == ']' || ch == '}') {
if (stack.isEmpty()) returnfalse;
stack.pop(); // simplified — ignoring mismatch for this demo
}
}
return true; // ❌ BUG: returns true even when '(((' is the input!// Fix: return stack.isEmpty();
}
// ❌ BUGGY VERSION 2: Popping without checking isEmpty firstpublicstaticbooleanbuggyNoEmptyGuard(String input) {
Stack<Character> stack = newStack<>();
for (char ch : input.toCharArray()) {
if (ch == '(' || ch == '[' || ch == '{') {
stack.push(ch);
} elseif (ch == ')' || ch == ']' || ch == '}') {
char top = stack.pop(); // ❌ BUG: crashes with EmptyStackException if input starts with ')'// Fix: if (stack.isEmpty()) return false; ← add this line BEFORE calling pop()
}
}
return stack.isEmpty();
}
publicstaticvoidmain(String[] args) {
// Demonstrate Bug 1: '(((' has 3 openers and no closersSystem.out.println("Bug 1 — '(((' should be UNBALANCED:");
System.out.println("Buggy result: " + buggyNoEmptyCheck("(((")); // prints true — WRONG// Demonstrate Bug 2: input starts with ')'System.out.println("\nBug 2 — ')))' should be UNBALANCED:");
try {
System.out.println("Buggy result: " + buggyNoEmptyGuard(")))"));
} catch (java.util.EmptyStackException e) {
System.out.println("CRASHED with EmptyStackException! Fix: check isEmpty() before pop()");
}
// Show the correct version handles both gracefullySystem.out.println("\nCorrect isBalanced('((('): " + BalancedParenthesesChecker.isBalanced("((("));
System.out.println("Correct isBalanced('))): " + BalancedParenthesesChecker.isBalanced(")))"));
}
}
Output
Bug 1 — '(((' should be UNBALANCED:
Buggy result: true
Bug 2 — ')))' should be UNBALANCED:
CRASHED with EmptyStackException! Fix: check isEmpty() before pop()
Correct isBalanced('((('): false
Correct isBalanced(')))'): false
Watch Out:
The empty string '' is a valid edge case — and it IS balanced, because there are zero openers and zero closers, so nothing is mismatched. Your algorithm handles it correctly by default: the loop never runs, and isEmpty() returns true. Don't add a special early-return for empty strings — you'll accidentally change the correct behaviour.
Production Insight
The two bugs shown here are the most common reasons why a production linter or code formatter fails. I've personally debugged a build pipeline where '(((' passed validation and caused a runtime error later in the compilation chain. The fix is always the same: return stack.isEmpty() and guard pop with isEmpty().
The third bug (String == comparison) is rarer but nastier — it passes tests with small strings because of string interning, then fails unpredictably with dynamic values. Always use char primitives or .equals() for String.
Key Takeaway
There are exactly two bugs that matter: missing final isEmpty() and missing guard before pop().
Both are one-line fixes.
If your code has these, it's wrong. Full stop.
Interview Prep: Variations Interviewers Add to the Core Problem
Once you've nailed the base algorithm, interviewers don't stop there. They mutate the problem to see if you truly understand it or just memorised the solution. Here are the three most common extensions, with the key insight for each.
Variation 1: 'What if the string contains non-bracket characters like letters and numbers?' The answer is: ignore them. Your loop only acts when a character is a bracket. Everything else passes through. The code already handles this — add any character to a test string and verify it yourself.
Variation 2: 'Can you solve this without a stack?' Yes, but only if the input uses a single type of bracket — say, only parentheses. In that case you can use a counter: increment for '(', decrement for ')'. If counter goes negative, return false. If counter is zero at the end, return true. This breaks immediately with multiple bracket types because you lose ordering information.
Variation 3: 'What's the minimum number of bracket additions or removals to make a string balanced?' This is a harder follow-up (LeetCode 921). The trick is to track two counters — open (unmatched openers) and close (unmatched closers) — and return their sum at the end. Knowing this follow-up exists and describing the approach impresses interviewers even if you don't code it live.
The code below implements the counter shortcut for single-bracket strings, so you can see where it works — and understand exactly why it breaks with mixed brackets.
SingleBracketCounterApproach.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package io.thecodeforge;
publicclassSingleBracketCounterApproach {
/**
* Counter-only approach — ONLY works correctly for a single bracket type.
* Fails silently for mixed brackets like '([)]'.
* UsethisONLY when you're certain the input has one bracket type.
*/
publicstaticbooleanisBalancedSingleType(String input) {
int openCount = 0; // tracks how many unmatched '(' we've seen so farfor (char ch : input.toCharArray()) {
if (ch == '(') {
openCount++; // found an opener — remember it
} elseif (ch == ')') {
openCount--; // found a closer — cancel one openerif (openCount < 0) {
return false; // more closers than openers so far — can't recover
}
}
}
// openCount == 0 means all openers were matchedreturn openCount == 0;
}
publicstaticvoidmain(String[] args) {
// Works correctly for parentheses-only stringsSystem.out.println("'(())' → " + isBalancedSingleType("(())")); // trueSystem.out.println("'())(' → " + isBalancedSingleType("())(" )); // falseSystem.out.println("'(((' → " + isBalancedSingleType("(((")); // false// ❌ Where the counter approach SILENTLY gives wrong answers// '([)]' has 2 openers and 2 closers, but they're in wrong order// Counter sees: +1, +1(ignored [), -1, -1(ignored ]) = 0 → reports BALANCED// But it's actually UNBALANCED — use the stack solution for mixed brackets!System.out.println("\n'([)]' counter result (WRONG): " + isBalancedSingleType("([)]"));
System.out.println("'([)]' stack result (CORRECT): " + BalancedParenthesesChecker.isBalanced("([)]"));
}
}
Output
'(())' → true
'())(' → false
'(((' → false
'([)]' counter result (WRONG): true
'([)]' stack result (CORRECT): false
Interview Gold:
If an interviewer asks 'can you do this in O(1) space?', the honest answer for the general multi-bracket problem is no — you need the stack, which is O(n) space. But for single-bracket inputs the counter trick achieves O(1) space. Knowing when to use which solution — and why — is exactly what separates a junior answer from a senior one.
Production Insight
In production, you rarely know the input format in advance. A general parser must assume multiple bracket types. The counter shortcut is only safe if you control the input (e.g., a closed system where only parentheses are used). In a linter or compiler, always use the stack. The silent wrong answer from the counter is worse than an obvious crash — it leads to invalid code being accepted and causing runtime errors.
Key Takeaway
Stack is the general solution. Counter is a memory optimization for single-type inputs only.
Never use a counter for mixed brackets — it silently gives wrong answers.
If asked about O(1) space, mention the counter trick but explain its limitation.
● Production incidentPOST-MORTEMseverity: high
The Linter That Let Unmatched Brackets Through
Symptom
React app rendered blank white page with no console errors. Bundle compiled without warnings.
Assumption
The linter's bracket check was considered robust because it passed all unit tests with single-type brackets.
Root cause
The linter used a simple counter approach for all bracket types. In the JSX, a combination of curly braces and parentheses created a '([)]' pattern that the counter accepted as balanced. The actual JSX was syntactically invalid, but the linter never caught it.
Fix
Replaced the counter with a proper stack-based validator that checks matching pairs. Also added integration tests with mixed bracket types.
Key lesson
Never use a counter for multiple bracket types — it loses ordering information.
Always validate your bracket checker against edge cases like '([)]' and '(('.
Production linters should use a stack, not a simplified heuristic.
Production debug guideSymptom → Action guide for when your bracket checker gives wrong results4 entries
Symptom · 01
Your validator returns 'balanced' for '([)]'
→
Fix
Check if you're using a counter instead of a stack. Counter loses order. Switch to stack with explicit matching pairs.
Symptom · 02
EmptyStackException thrown at runtime
→
Fix
Your input started with a closing bracket. Add an isEmpty() guard before every pop(). If empty, return false immediately.
Symptom · 03
Unmatched openers like '(((' return 'balanced'
→
Fix
You're returning true at the end of the loop without checking if the stack is empty. Fix: return stack.isEmpty() instead of return true.
Symptom · 04
Validator fails on strings with non-bracket characters
→
Fix
Check if you're pushing non-bracket characters onto the stack. Your loop should only react to bracket characters; ignore everything else.
★ Quick Debug: Stack-Based Bracket CheckerWhen your bracket checker misbehaves, run these commands and checks to isolate the bug.
Wrong result for '([)]'−
Immediate action
Print stack after each push and pop
Commands
Add System.out.println("Stack: " + stack) after each push/pop
Trace '([)]' manually and compare with printed output
Fix now
Ensure you're checking the top of stack against the matching pair, not just popping blindly
Crash on input starting with ')'+
Immediate action
Check if pop() is called on empty stack
Commands
Add if (stack.isEmpty()) return false; before every pop()
Add a debug print "Empty stack at index X" inside that guard
Fix now
Return false when stack is empty and a closing bracket is encountered
Empty string returns false+
Immediate action
Check return statement
Commands
Ensure the final return is stack.isEmpty() not return false
Return stack.isEmpty() — empty string has zero openers, so stack is empty → true
Stack Approach vs Counter Approach
Aspect
Stack Approach (General)
Counter Approach (Single Bracket Only)
Supports multiple bracket types
Yes — handles '(', '[', '{' together
No — gives wrong answers silently
Detects wrong ordering like '([)]'
Yes — catches it immediately
No — counts match so it returns true incorrectly
Time Complexity
O(n) — one pass through the string
O(n) — one pass through the string
Space Complexity
O(n) — worst case all chars on stack
O(1) — just one integer counter
Handles empty string
Yes — returns true correctly
Yes — returns true correctly
Fails fast on first mismatch
Yes — returns false immediately
Yes — returns false when count goes negative
Interview recommended?
Always — it is the correct general solution
Only mention as a follow-up optimization for limited inputs
Key takeaways
1
A stack's LIFO property is not accidental here
it directly mirrors bracket nesting, where the most recently opened bracket must always be the first one closed.
2
There are exactly two valid reasons to return false mid-loop
the stack is empty when you see a closing bracket, or the top of the stack doesn't match the closing bracket you just read.
3
The final isEmpty() check is not optional
a string full of unclosed openers like '(((' will pass the entire loop without error and needs that last check to be caught.
4
The counter shortcut (O(1) space) only works for single-bracket inputs
it silently gives wrong answers for mixed brackets because it loses all ordering information.
Common mistakes to avoid
3 patterns
×
Forgetting the final isEmpty() check
Symptom
String '(((' passes the loop without error because the code returns true unconditionally after the loop, so it incorrectly reports BALANCED.
Fix
Change the final return statement from 'return true;' to 'return stack.isEmpty();' — this ensures any unmatched openers are detected.
×
Calling pop() without first checking isEmpty()
Symptom
Input like ')))' has a closing bracket before any opener. When the code tries to pop from an empty stack, Java throws an EmptyStackException and crashes the program instead of returning false gracefully.
Fix
Add an 'if (stack.isEmpty()) return false;' guard immediately before every pop() call. This handles the case where a closer appears without any opener.
×
Using the counter trick for multi-bracket strings
Symptom
String '([)]' has equal numbers of openers and closers, so a counter reaches zero and reports BALANCED — but the order is wrong. This results in silent acceptance of invalid code in production.
Fix
Use the stack approach whenever the input can contain more than one type of bracket. Reserve the counter only for problems that are guaranteed to use a single bracket type (e.g., LeetCode 20 but only parentheses).
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01JUNIOR
Given the string '{[()]}', walk me through your algorithm step by step —...
Q02SENIOR
What are the time and space complexities of your solution, and is there ...
Q03SENIOR
How would you modify your solution to also return the index of the first...
Q01 of 03JUNIOR
Given the string '{[()]}', walk me through your algorithm step by step — what's on the stack after each character?
ANSWER
Initialize empty stack. Character '{' -> push. Stack: ['{']. Character '[' -> push. Stack: ['{', '[']. Character '(' -> push. Stack: ['{', '[', '(']. Character ')' -> closing, pop top '(' matches ')', stack: ['{', '[']. Character ']' -> closing, pop top '[' matches ']', stack: ['{']. Character '}' -> closing, pop top '{' matches '}', stack empty. End of string, stack is empty -> balanced.
Q02 of 03SENIOR
What are the time and space complexities of your solution, and is there any way to reduce the space complexity?
ANSWER
Time is O(n) because we scan each character once and push/pop are O(1). Space is O(n) in the worst case (all opening brackets, no closers). If the input uses only a single bracket type (say only parentheses), you can reduce space to O(1) using a counter: increment on '(', decrement on ')', check count never negative and ends at 0. But for multiple bracket types, the stack is necessary because it preserves ordering information. There is no known O(1) space solution for the general multi-bracket problem.
Q03 of 03SENIOR
How would you modify your solution to also return the index of the first unmatched bracket, rather than just true or false?
ANSWER
We need to track the index as we iterate. When we detect a mismatch (either stack empty on closer, or top doesn't match, or stack non-empty at end), we return the current index (for mid-string failures) or the index of the topmost opener in the stack (for end-of-string failures). For mid-string failures, the current character's index is the first unmatched position. For end-of-string failures, we need to pop the stack to find the earliest unmatched opener, but the most reliable approach is to also store the index alongside each bracket when pushing. Then if stack non-empty at end, we pop and take the minimum index stored. The modified algorithm runs in O(n) time and O(n) space but returns an int index instead of boolean. Return -1 if balanced.
01
Given the string '{[()]}', walk me through your algorithm step by step — what's on the stack after each character?
JUNIOR
02
What are the time and space complexities of your solution, and is there any way to reduce the space complexity?
SENIOR
03
How would you modify your solution to also return the index of the first unmatched bracket, rather than just true or false?
SENIOR
FAQ · 7 QUESTIONS
Frequently Asked Questions
01
What is the balanced parentheses problem in data structures?
The balanced parentheses problem asks you to determine whether every opening bracket in a string has a corresponding closing bracket in the correct order. A string like '({[]})' is balanced, while '([)]' is not — even though it has equal numbers of openers and closers, the order is wrong. A stack is the standard data structure used to solve it.
Was this helpful?
02
Why do we use a stack to solve the balanced parentheses problem?
A stack follows Last In, First Out order, which perfectly matches how brackets nest. When you open a bracket, you don't know what closes it yet, so you push it and wait. The moment you see a closing bracket, the most recently opened bracket — sitting on top of the stack — is the one that must match. No other data structure captures this nested ordering as naturally.
Was this helpful?
03
Is an empty string considered balanced?
Yes. An empty string contains zero opening brackets and zero closing brackets, so every opener (there are none) has a matching closer. The algorithm handles this correctly by default: the loop body never executes, and the final isEmpty() check on an empty stack returns true.
Was this helpful?
04
What happens if we encounter a closing bracket when the stack is empty?
Return False immediately — there is no opening bracket to match. This handles strings like ')(' or ')))'.
Was this helpful?
05
How do you count the minimum number of additions to make a string balanced?
Track open_count (unmatched '(' so far) and close_count (unmatched ')' so far). For '(': open_count++. For ')': if open_count>0: open_count-- (match); else close_count++. Answer: open_count + close_count — add that many brackets.
Was this helpful?
06
Can this problem be solved without a stack?
For strings with only one type of bracket (e.g., only parentheses), a counter suffices: increment for '(', decrement for ')'. If counter goes negative or ends non-zero, return False. For multiple bracket types, a counter cannot distinguish '([)]' from '([])' — you need the stack to track the expected closing bracket.
Was this helpful?
07
What is the minimum number of bracket flips to make a string valid?
Scan left to right maintaining a counter starting at 0. Increment for '(', decrement for ')'. When counter goes negative, we need a flip: increment counter by 2 (one ')' becomes '(') and increment flip count. Final answer: flip_count + counter/2 (remaining unmatched '(' each need a ')' added, but adding is different from flipping; for the flip variant, result = flip_count + counter//2).