Hello! Welcome to the sixth lesson in our "Data Structures & Algorithmic Analysis" module.
In our previous lesson, we defined stacks and queues as Abstract Data Types and explored how to implement them using both arrays and linked lists. We focused on the crucial difference in their access patterns: LIFO (Last-In, First-Out) for stacks and FIFO (First-In, First-Out) for queues.
Today, we'll put that theory into practice. Our goal is to apply a stack to solve a classic computer science problem: checking for balanced parentheses. This is a perfect demonstration of how a problem's inherent structure can naturally lead to choosing the right data structure, a key skill in developing an "architectural mindset." You'll see exactly why the LIFO property of a stack makes it the ideal tool for this job.
1. Understanding the "Balanced Parentheses" Problem
First, let's clearly define what "balanced" means. You're given a string containing only bracket characters: (), [], and {}. The string is considered valid or "balanced" if it meets three specific rules.
To understand these rules and see examples of valid and invalid strings, please read the introductory section of the following article.
20. Valid Parentheses - In-Depth Explanation
The article '20. Valid Parentheses - In-Depth Explanation' on Algo.monster clearly lays out the rules for a valid bracket string.
Read the 'Problem Description' section to understand the three rules for a valid string and review the examples provided.
In summary, a balanced string requires that:
- Every opening bracket is closed by the same type of bracket.
- Brackets are closed in the correct order (e.g.,
([)]is invalid). - Every closing bracket has a corresponding opening bracket.
2. Developing the Intuition: Why a Stack?
Before we even write any code, think about how you would manually check a string like ({[]}). As you scan from left to right, you see an opening {. You remember it. Then you see (. You remember that too. Then [. You remember it.
Now you see a ]. Which bracket does it need to match? It must match the [, which was the most recent opening bracket you saw. Once they match, you can effectively "forget" about them and focus on the remaining open brackets: { and (.
Next, you see ). It must match the (, which is now the most recent opener. Finally, you see }. It must match the {. Since all brackets are matched, the string is balanced.
This pattern of matching the most recently opened, unclosed bracket is the key insight. What data structure is designed around accessing the "most recent" item? The stack, with its LIFO principle.
The following video provides an excellent walkthrough of this exact thought process, building the intuition that leads directly to the stack-based solution.
The Balanced Parentheses Problem - Classic Stack Problem ("Valid Parentheses" on Leetcode)
The video 'The Balanced Parentheses Problem' from Back To Back SWE does a great job of verbalizing the intuitive process of solving this problem, connecting it directly to the LIFO behavior of a stack.
Watch from 02:10 to 05:27. Pay close attention to the argument about needing to match the 'most recent left parenthesis'. This is the core logic that makes a stack the perfect tool.
3. The Stack-Based Algorithm
Now, let's formalize this intuition into a concrete algorithm:
- Create an empty stack.
- Iterate through each character of the input string.
- If the character is an opening bracket (
(,[, or{), push it onto the stack. - If the character is a closing bracket (
),], or}):
a. Check if the stack is empty. If it is, there's no matching opening bracket, so the string is invalid.
b. If the stack is not empty, pop the top element. Check if this popped bracket is the corresponding opening bracket for the current closing bracket. If they don't match, the string is invalid. - After the loop finishes, check if the stack is empty.
a. If it's empty, every opening bracket found a matching closing bracket. The string is valid.
b. If it's not empty, there are unclosed opening brackets left over. The string is invalid.
Let's see this algorithm in action.

To see a dynamic walkthrough of this process, let's return to the video.
The Balanced Parentheses Problem - Classic Stack Problem ("Valid Parentheses" on Leetcode)
This segment of the Back To Back SWE video demonstrates the algorithm step-by-step, showing exactly how elements are pushed and popped from the stack as the string is processed.
Watch from 05:27 to 07:27. Observe how the stack keeps track of the 'pending' open brackets and how mismatches are detected instantly.
4. Implementation Details and Common Pitfalls
Knowing the algorithm is the first step; implementing it correctly requires handling the edge cases we identified. This is where many developers make small but critical mistakes.
To ensure you build a robust solution, please read the following sections from the Algo.monster article. They provide a clear implementation strategy and, most importantly, highlight common errors to avoid.
20. Valid Parentheses - In-Depth Explanation
This article provides a concise implementation guide and a crucial section on common mistakes, which is invaluable for practical application.
Read the sections 'Solution Approach', 'Example Walkthrough', and 'Common Pitfalls'. Focus on the three major pitfalls: forgetting to check for an empty stack before popping, complex matching logic, and forgetting the final stack check.
To summarize the key failure points you must handle:
- Case 1: Unmatched Closing Bracket: A string like
()). When you encounter the second), the stack will be empty. Your code must checkif stack.isEmpty()before trying topop(). - Case 2: Mismatched Bracket Types: A string like
([)]. When you encounter the), you pop[from the stack. Your code must check if the popped element and the current character form a valid pair. - Case 3: Unclosed Opening Bracket: A string like
((. After the loop, the stack will contain two(. Your code must returnstack.isEmpty()at the end to catch this.
5. Complexity Analysis
Let's quickly analyze the performance of this algorithm.
-
Time Complexity:
- We iterate through the string of length exactly once.
- Each operation inside the loop—pushing, popping, and checking the top of the stack—is an operation.
- Therefore, the total time complexity is linear with respect to the length of the input string.
-
Space Complexity:
- In the worst-case scenario, the string consists of only opening brackets (e.g.,
((((...)). - In this case, we would push all characters onto the stack.
- Therefore, the space required by the stack is, in the worst case, proportional to the length of the input string.
- In the worst-case scenario, the string consists of only opening brackets (e.g.,
Test your understanding!
Your colleague proposes a "simplified" version of the balanced parentheses algorithm. Instead of a stack, it uses three counters: round_brackets, square_brackets, and curly_brackets. It iterates through the string, incrementing the respective counter for an opening bracket and decrementing for a closing one. If any counter ever drops below zero, or if any counter is not zero at the end, the string is declared invalid.
Would this algorithm work correctly? If not, provide a simple example of a string that would fool this algorithm and explain why.
Show answer
No, this algorithm would not work correctly.
It fails because it only tracks the counts of brackets, not their order or nesting.
A simple counter-example that would fool this algorithm is ([)].
- The algorithm would process it as follows:
(:round_bracketsbecomes 1.[:square_bracketsbecomes 1.):round_bracketsbecomes 0.]:square_bracketsbecomes 0.
At the end, all counters are zero, and they never went below zero. The counter-based algorithm would incorrectly declare this string as "balanced." However, we know it's invalid because the brackets are not closed in the correct LIFO order. This demonstrates why a stack, which preserves order, is essential.
Conclusion
In this lesson, you have successfully applied the stack data structure to solve a practical problem. This exercise is fundamental to bridging the gap between theoretical data structures and real-world problem-solving.
Key Takeaways:
- The "balanced parentheses" problem is a classic example of a structure that requires LIFO processing.
- The core algorithmic pattern involves pushing opening brackets and popping/matching them with closing brackets.
- A robust solution must handle three key edge cases: unmatched closers, mismatched types, and unclosed openers.
- The solution has a time complexity of and a space complexity of .
Next Steps
You've seen how a stack's LIFO nature is critical for nested structures. In our next lesson, we will shift our focus to the queue and its FIFO property. We'll solve another classic problem that requires processing elements in the order they were received, providing a clear contrast to the stack's behavior and further solidifying your ability to choose the right tool for the job.