Hello! Welcome to your next lesson in our module on Stacks, Queues, and Deques.
In the previous lesson, you successfully built stack data structures from the ground up using both arrays and linked lists. You now have a solid understanding of the Last-In, First-Out (LIFO) principle and the O(1) efficiency of core stack operations.
Today, we'll put that knowledge into practice. Your learning outcome is to solve the balanced parentheses problem using a stack. This is a quintessential computer science problem, frequently used in technical interviews to test your grasp of basic data structures. It has practical applications in parsing code, evaluating mathematical formulas, and validating structured data formats like JSON.
1. What Makes Parentheses "Balanced"?
Before diving into the solution, let's be crystal clear about the problem. We are given a string containing three types of brackets: (), {}, and []. The string is "balanced" if two conditions are met:
- Every opening bracket has a corresponding closing bracket of the same type.
- The brackets are correctly nested. The most recently opened bracket must be the first one to be closed.

The core rule—"the most recently opened bracket must be the first one to be closed"—should sound familiar. It perfectly describes the LIFO behavior of a stack. This is the key insight to solving the problem efficiently.
Let's watch a short video to see this logic in action.
Data Structures: Balanced Parentheses in Expression
Gayle Laakmann McDowell, author of 'Cracking the Coding Interview', provides an excellent walkthrough of the problem's logic. This video directly addresses the kind of thinking expected in a technical interview.
Watch from the beginning to 02:43. Pay close attention to how she identifies that opening brackets are always fine, but closing brackets must match the 'live' or most recent opening bracket. This naturally leads to the idea of using a stack.
2. The Algorithm: Using a Stack
As the video suggests, a stack is the ideal tool for this job. The algorithm is straightforward and elegant:
- Initialize an empty stack. We'll use this to keep track of the opening brackets we've seen so far.
- Iterate through the input string, one character at a time.
- If the character is an opening bracket (
(,{, or[): Push it onto the stack. - If the character is a closing bracket (
),}, or]):- First, check if the stack is empty. If it is, this means we've found a closing bracket with no corresponding opening bracket. The string is unbalanced, so we can stop and return
false. - If the stack is not empty,
popthe character from the top. - Compare the popped opening bracket with the current closing bracket. If they do not form a matching pair (e.g.,
(and]or{and)), the string is unbalanced. Returnfalse.
- First, check if the stack is empty. If it is, this means we've found a closing bracket with no corresponding opening bracket. The string is unbalanced, so we can stop and return
- After the loop finishes: If we've processed the entire string, we have one final check. The stack must be empty. If it's not, it means there are opening brackets that were never closed. The string is unbalanced, so return
false. - If the stack is empty at the end, it means every bracket was perfectly matched and nested. The string is balanced, so return
true.
Let's visualize this with a dry run.

To further solidify this, the following video provides a fantastic animated walkthrough.
Valid Parentheses (LeetCode 20) | Full solution with visuals and animations | Stack Data Structure
The video 'Valid Parentheses' by Nikhil Lohia uses clear animations to show how the stack grows and shrinks as the string is processed. This will help you build a strong mental model of the algorithm.
Watch from 05:55 to 11:22. The first part (until 09:58) demonstrates the algorithm on a valid string. The second part (from 09:58) shows how the algorithm correctly identifies an invalid string. This contrast is very important.
3. Java Implementation
Now, let's translate this algorithm into Java code. Given your background, you'll appreciate how a Map can make the code for checking bracket pairs clean and easily extensible.
In the previous lesson, we discussed java.util.Stack and how it's a legacy, thread-safe class built on Vector. For single-threaded applications like this one, the modern and preferred interface for stack operations is java.util.Deque, with java.util.ArrayDeque being the standard implementation. It's faster and more conventional for interview settings.
Here is a complete and commented implementation.
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
import java.util.HashMap;
public class BalancedParentheses {
public static boolean isValid(String s) {
// Use ArrayDeque as our stack implementation.
Deque<Character> stack = new ArrayDeque<>();
// Map to hold the relationships between closing and opening brackets.
// This is cleaner than a long if/else or switch statement.
Map<Character, Character> bracketMap = new HashMap<>();
bracketMap.put(')', '(');
bracketMap.put('}', '{');
bracketMap.put(']', '[');
// Iterate through each character of the string.
for (char c : s.toCharArray()) {
// If the character is a closing bracket...
if (bracketMap.containsKey(c)) {
// Check if the stack is empty. If so, there's no matching opener.
// Also, check if the top of the stack matches the expected opener.
// We use a placeholder '#' if the stack is empty to avoid a NullPointerException
// and ensure the comparison fails safely.
char topElement = stack.isEmpty() ? '#' : stack.pop();
if (topElement != bracketMap.get(c)) {
return false; // Mismatch
}
} else {
// If it's an opening bracket, push it onto the stack.
stack.push(c);
}
}
// After the loop, if the stack is empty, all brackets were matched.
// If not empty, there are unclosed opening brackets.
return stack.isEmpty();
}
public static void main(String[] args) {
String expr1 = "()[]{}";
String expr2 = "([)]";
String expr3 = "{[]}";
String expr4 = "((())";
System.out.println(expr1 + " is balanced: " + isValid(expr1)); // true
System.out.println(expr2 + " is balanced: " + isValid(expr2)); // false
System.out.println(expr3 + " is balanced: " + isValid(expr3)); // true
System.out.println(expr4 + " is balanced: " + isValid(expr4)); // false
}
}
For more examples and a slightly different coding style, you can review the provided articles.
Valid Parentheses in O(n) Time Complexity using Java
The article 'Valid Parentheses in O(n) Time Complexity using Java' from AlgoCademy provides another clean implementation and discusses important edge cases for testing, which is crucial for interview prep.
Skim through the 'Optimized Solution', 'Algorithm', 'Code Implementation', and 'Edge Cases' sections. Notice the use of a helper function isMatchingPair, which is another good way to modularize the code.
Complexity Analysis
- Time Complexity: O(n)
We iterate through the input string of length n exactly once. Each stack operation (pushandpop) takes constant time, O(1). - Space Complexity: O(n)
In the worst-case scenario, the input string consists only of opening brackets (e.g.,((((((). In this case, we would have to store all n characters on the stack.
Test your understanding!
For each of the following input strings, trace the state of the stack and determine if the final result is true or false.
s = "()[]{}"s = "([)]"s = "){"
Show answer
-
s = "()[]{}"->true(: push(-> stack:[(]): pop(, matches -> stack:[][: push[-> stack:[[]]: pop[, matches -> stack:[]{: push{-> stack:[{]}: pop{, matches -> stack:[]- End of string, stack is empty. Result:
true.
-
s = "([)]"->false(: push(-> stack:[(][: push[-> stack:[,(]`): pop[. It does not match). Result:false. The algorithm stops here.
-
s = "){"->false): This is a closing bracket. The stack is empty. Result:false. The algorithm stops here.
Conclusion
Fantastic! You've successfully applied the stack data structure to solve a classic and practical problem. This is a big step in moving from knowing what a data structure is to understanding how and why to use it.
Key Takeaways:
- The balanced parentheses problem is a perfect use case for a stack due to its LIFO (Last-In, First-Out) nature.
- The core algorithm involves pushing opening brackets onto the stack and popping to match closing brackets.
- Three conditions can lead to an "unbalanced" result:
- Encountering a closing bracket when the stack is empty.
- Popping an opening bracket that doesn't match the current closing bracket.
- The stack is not empty after processing the entire string.
- The solution is highly efficient, with O(n) time and O(n) space complexity.
Preview of the next lesson:
Now that you're comfortable with this standard stack application, we'll level up. In the next lesson, we will explore a more advanced stack pattern called a monotonic stack to solve the "next greater element" problem. This pattern adds a specific ordering constraint to the elements within the stack, allowing you to solve another class of common interview questions.