Hello! In our last lesson, we focused on the practical aspects of using Java's built-in collections, specifically concluding that Deque implemented by ArrayDeque is the modern and preferred choice for stack operations. Today, we'll put that knowledge into action by tackling a classic interview problem that perfectly illustrates the power and utility of the stack data structure.
Our goal is to solve the 'Valid Parentheses' problem. This problem is a staple in technical interviews because it directly tests your understanding of the Last-In-First-Out (LIFO) principle and your ability to apply the correct data structure to a seemingly simple string manipulation task. By the end of this lesson, you'll be able to identify the pattern, implement a robust solution in Java, and analyze its efficiency.
1. Understanding the 'Valid Parentheses' Problem
First, let's define what we're trying to solve. You are given a string containing only the characters '(', ')', '{', '}', '[', and ']'. Your task is to determine if the input string is "valid."
A string is considered valid if it meets three key conditions:
- Same Type Matching: Every opening bracket must be closed by the same type of bracket. For example,
(must be closed by). - Correct Order: Open brackets must be closed in the correct order. The most recently opened bracket must be closed first.
- Complete Pairing: Every closing bracket must have a corresponding opening bracket, and vice-versa.
Let's look at some examples to make this concrete.

To formalize this, please review the problem description from NeetCode.
20. Valid Parentheses - Solution & Explanation
This section on NeetCode provides a clear and concise definition of the problem, along with several examples.
Read the entire problem description, paying close attention to the three examples provided. This will solidify your understanding of the rules.
2. From Brute-Force to Optimal
As you prefer, let's start by considering a brute-force approach before finding a more elegant, optimal solution.
Brute-Force Approach
A straightforward way to think about this is that any valid pair of adjacent brackets, like (), [], or {}, can be "removed" from the string without changing its overall validity. For example, in ([{}]), the {} pair is a valid, innermost unit. If we remove it, we are left with ([]). Then, [] is another valid unit. Removing it leaves (). Finally, removing () leaves an empty string. An empty string at the end implies the original was valid.
This leads to a simple, though inefficient, algorithm.
20. Valid Parentheses - Solution & Explanation
The NeetCode article explains this exact brute-force method.
Read the section titled "Brute Force". The intuition is based on repeatedly removing valid pairs. Note the Java implementation and the analysis of its time complexity, which is O(n^2). This quadratic complexity arises from repeatedly scanning and modifying the string, which is inefficient for large inputs.
While this works, an O(n^2) solution is unlikely to be accepted in an interview for this problem. We need a more efficient way to handle the "correct order" rule, which is the key challenge.
The Optimal Stack-Based Approach
The "correct order" rule — that the most recently opened bracket must be closed first — is a powerful hint. This "Last-In, First-Out" (LIFO) behavior is the defining characteristic of a stack.
Let's watch a short clip from "take U forward" (Striver's channel) that crystallizes this intuition.
L2. Check for Balanced Parentheses | Stack and Queue
This video explains why the nature of the problem points directly to using a stack.
Watch the segment explaining the core idea. The key insight is that when you see a closing bracket, you need to check the "last opening encountered", which is precisely what a stack provides.
This insight leads to our optimal algorithm:
- Initialize an empty stack. As we learned, we'll use
Deque<Character> stack = new ArrayDeque<>();. - Iterate through the input string character by character.
- If the character is an opening bracket (
(,{,[), push it onto the stack. We are "remembering" this opening bracket, as it needs to be closed later. - If the character is a closing bracket (
),},]):- Check if the stack is empty. If it is, we have a closing bracket with no corresponding opener. The string is invalid.
- If the stack is not empty, pop the top element.
- Compare the popped opening bracket with the current closing bracket. If they do not form a matching pair (e.g.,
)with{), the string is invalid.
- After iterating through the entire string, we must perform one final check: Is the stack empty?
- If it is, every opening bracket was correctly matched and closed. The string is valid.
- If it is not empty, it means there are unclosed opening brackets (e.g.,
s = "((("). The string is invalid.
Let's see this algorithm in action.

For a more dynamic walkthrough, the take U forward video provides excellent dry runs for both valid and invalid strings.
L2. Check for Balanced Parentheses | Stack and Queue
This part of the video walks through the stack-based algorithm step-by-step.
Watch the detailed dry runs for both a balanced string and several unbalanced cases. This will help you see how the algorithm handles mismatches, unclosed brackets, and premature closing brackets.
3. Java Implementation and Common Pitfalls
Now, let's look at a clean Java implementation. As we established in the last lesson, using Deque and ArrayDeque is the modern standard.
20. Valid Parentheses - In-Depth Explanation
This implementation from AlgoMonster correctly uses Deque and ArrayDeque, following modern Java best practices.
Please review the Java code block. Notice the declaration Deque<Character> stack = new ArrayDeque<>(); and how the push, pop, and peek methods are used exactly as we discussed. The helper method isMatchingPair is a clean way to handle the matching logic.
Finally, to be well-prepared for interviews, it's crucial to know the common mistakes and edge cases.
20. Valid Parentheses - Solution & Explanation
This section from NeetCode lists the most frequent errors developers make when solving this problem.
Read the section on "Common Pitfalls". Pay close attention to these three key points: Checking for an empty stack before popping: Trying to pop() from an empty stack will cause a runtime error. You must always check !stack.isEmpty() first. Checking if the stack is empty at the end: A common mistake is to return true right after the loop finishes. You must check if the stack is empty to handle cases like "(". Correctly mapping brackets: Ensure your logic correctly maps a closing bracket to its corresponding opening bracket.
4. Complexity Analysis
- Time Complexity: O(n)
We iterate through the input string of lengthnexactly once. Each operation inside the loop (push, pop, peek) is a constant timeO(1)operation for a stack. - Space Complexity: O(n)
In the worst-case scenario, where the string consists of only opening brackets (e.g.,"((([{{...}}]))))"), the stack will have to store allncharacters. Therefore, the space required is proportional to the length of the string.
Conclusion
In this lesson, you've applied your knowledge of stacks to solve a fundamental interview problem. You've seen how to progress from a brute-force idea to an efficient, linear-time solution by recognizing the underlying LIFO pattern.
Key Takeaways:
- The "Valid Parentheses" problem is a classic example of a problem best solved with a stack.
- The LIFO (Last-In, First-Out) nature of bracket matching is the key insight.
- The algorithm involves pushing opening brackets onto the stack and popping them to match with corresponding closing brackets.
- Crucial edge cases to handle are:
- An empty stack when a closing bracket is found.
- A non-empty stack after the entire string has been processed.
- The optimal solution has a time complexity of
O(n)and a space complexity ofO(n).
In our next lesson, we will explore a more advanced and powerful technique involving stacks: the monotonic stack. We will apply this pattern to solve the "Next Greater Element" problem, which builds directly on the foundations we've established today.