DSA Series Day 13 Valid Parentheses in Java, Python and Rust

DSA Series Day 13: Valid Parentheses in Java, Python and Rust

Valid Parentheses is the friendliest stack problem out there, and it is Day 13 of the Tekraze DSA series. We are done with sliding windows and now we step into stacks. Almost every interview prep list starts with this problem for a good reason, and once it clicks, a whole family of matching and nesting problems becomes easy.

Also Read: Day 12, Minimum Window Substring wraps up the sliding window section that comes right before this one.

What is the Valid Parentheses problem?

You get a string s made only of the characters ( ) [ ] { }. Return true if the string is valid. It is valid when every opening bracket is closed by the same type of bracket, brackets close in the correct order, and every closing bracket has a matching opener. You can also try it on LeetCode, where it is problem 20.

Valid Parentheses examples

text

Input:  s = "()[]{}"
Output: true
Input:  s = "([)]"
Output: false
Why:    "[" was opened last, so it must close before ")"
Input:  s = "{[]}"
Output: true

How should you think about it in plain words?

Think of stacking plates. Every time you see an opening bracket, you put a plate on top of the pile. Every closing bracket must take off the plate on top, and that plate has to be the same type.

Valid Parentheses plate analogy with a pile of plates and a hand removing the top one
DSA Series Day 13: Valid Parentheses in Java, Python and Rust 7

If the top plate is the wrong type, or there is no plate at all, the string is broken. When you reach the end, the pile must be empty, otherwise some brackets were never closed. That is the whole idea in four lines.

What does the brute force solution look like?

Keep deleting matched pairs like (), [] and {} until nothing changes. If the string ends up empty, it was valid.

python

def is_valid_brute(s):
    prev = None
    while prev != s:
        prev = s
        s = s.replace("()", "").replace("[]", "").replace("{}", "")
    return s == ""

Time is O(n²) because each pass scans the string and you may need about n/2 passes for deep nesting. Space is O(n) for the new strings each pass creates. It is cute and short, but it does far more work than needed.

How do we get the optimal Valid Parentheses stack solution?

Walk through the string once. For an opening bracket, push the closing bracket you expect to see later. For a closing bracket, pop the top of the stack and check that it equals the current character. If the stack is empty or the values differ, return false right away. At the end, return true only if the stack is empty.

Pushing the expected closer instead of the opener is a small trick that removes the need for a lookup when you pop. You just compare two characters.

Valid Parentheses push and pop flow drawn as a stack with arrows moving in and out
DSA Series Day 13: Valid Parentheses in Java, Python and Rust 8

The Valid Parentheses stack solution runs in O(n) time, since each character is pushed and popped at most once. Space is O(n) in the worst case, like a string of only opening brackets. For a quick refresher on reading these numbers, see our Big O helper post on time and space complexity.

Valid Parentheses in Java

Java’s ArrayDeque is the usual choice for a stack, since the old Stack class is slower and considered legacy.

java

import java.util.ArrayDeque;
import java.util.Deque;
public class Solution {
    public static boolean isValid(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for (char c : s.toCharArray()) {
            // push the closer we expect to see later
            if (c == '(') stack.push(')');
            else if (c == '[') stack.push(']');
            else if (c == '{') stack.push('}');
            else if (stack.isEmpty() || stack.pop() != c) return false;
        }
        return stack.isEmpty();
    }
    public static void main(String[] args) {
        String[] in = {"()", "()[]{}", "(]", "([)]", "{[]}", "", "(", "]", "(((", "([]){}"};
        boolean[] want = {true, true, false, false, true, true, false, false, false, true};
        for (int i = 0; i < in.length; i++) {
            boolean got = isValid(in[i]);
            System.out.println((got == want[i] ? "PASS " : "FAIL ") + "\"" + in[i] + "\" -> " + got);
        }
    }
}

Valid Parentheses in Python

In Python, a plain list works as a stack with append and pop.

python

def is_valid(s):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []
    for ch in s:
        if ch in pairs:
            # a closer must match the most recent opener
            if not stack or stack.pop() != pairs[ch]:
                return False
        else:
            stack.append(ch)
    return not stack
tests = [("()", True), ("()[]{}", True), ("(]", False), ("([)]", False), ("{[]}", True),
         ("", True), ("(", False), ("]", False), ("(((", False), ("([]){}", True)]
for s, want in tests:
    got = is_valid(s)
    print("PASS" if got == want else "FAIL", repr(s), "->", got)

Valid Parentheses in Rust

In Rust, a Vec does the job, and pop returns an Option, so an empty stack is handled safely.

rust

fn is_valid(s: &str) -> bool {
    let mut stack: Vec<char> = Vec::new();
    for c in s.chars() {
        match c {
            // push the closer we expect to see later
            '(' => stack.push(')'),
            '[' => stack.push(']'),
            '{' => stack.push('}'),
            _ => {
                if stack.pop() != Some(c) {
                    return false;
                }
            }
        }
    }
    stack.is_empty()
}
fn main() {
    let tests = [("()", true), ("()[]{}", true), ("(]", false), ("([)]", false), ("{[]}", true),
                 ("", true), ("(", false), ("]", false), ("(((", false), ("([]){}", true)];
    for (s, want) in tests {
        let got = is_valid(s);
        println!("{} {:?} -> {}", if got == want { "PASS" } else { "FAIL" }, s, got);
    }
}

Do all three Valid Parentheses solutions pass the tests?

All three programs ran on the same ten tests, including “()[]{}”, the crossed “([)]”, nested “{[]}”, an empty string, a lone opener, a lone closer and “(((”. Every test passed in Java, Python and Rust.

Also Read: Our data structures cheat sheet has a stack section that pairs nicely with this post.

What are the 6 tips and tricks for an easy solution?

  1. Spot the pattern. Whenever the most recent open thing must be closed first, think stack. Last in, first out is exactly the rule in Valid Parentheses.
  2. Push the expected closer. It turns the matching check into a single comparison and keeps the code tiny.
  3. Exit early on odd length. A string with an odd number of characters can never be valid, so you can return false before the loop.
  4. Check for an empty stack before popping. A closer with nothing to match is the most common hidden failure.
  5. Test the edges. Try the empty string, only openers, only closers, crossed pairs like “([)]” and deep nesting.
  6. Say the plate analogy out loud first. In an interview it shows you understand why a stack fits, not just that it works.

What are the common mistakes people make?

These are the slips that break most first attempts at Valid Parentheses:

  • Only counting brackets, which says “([)]” is valid even though the order is wrong.
  • Popping from an empty stack and crashing, or treating a missing value as a match.
  • Forgetting to check that the stack is empty at the end, so “((” passes.
  • Using a list as a queue by mistake and taking from the wrong end.
  • Building a big if else chain instead of a small map, which makes adding new bracket types painful.

Where is this pattern used in the real world?

Valid Parentheses is not just an interview exercise. Every code editor and compiler uses a stack to match brackets, which is how your editor highlights the partner of the bracket under your cursor and warns you about a missing one.

Valid Parentheses idea in a code editor with two matching brackets highlighted
DSA Series Day 13: Valid Parentheses in Java, Python and Rust 9

HTML and XML parsers check that tags open and close in the right order the same way. JSON validators, calculator apps that handle nested expressions and even the undo history in many apps all lean on the same last in, first out idea.

Which earlier posts in the series should you read?

If Valid Parentheses is your first stack problem, these earlier posts build the skills it leans on:

Conclusion

Valid Parentheses comes down to one rule: the most recent opener must close first. Push the expected closer, pop and compare on every closing bracket, and check the stack is empty at the end. Try rewriting it from memory, then run it against the ten tests above. Next up we keep going with stacks.

Frequently asked questions

What is the time complexity of Valid Parentheses?

The stack solution runs in O(n) time because each character is pushed and popped at most once. Space is O(n) in the worst case.

Why is a stack the right data structure for Valid Parentheses?

Brackets must close in the reverse order they opened. A stack gives you the most recently opened bracket first, which is exactly the one that must close next.

Can you solve Valid Parentheses without a stack?

If there is only one bracket type, a simple counter works. With mixed types like ( [ { you need a stack, or something that behaves like one, to remember the order.

Is an empty string valid?

Yes. An empty string has no unmatched brackets, so the usual expected answer is true.

What problems build on Valid Parentheses?

Min Stack, Evaluate Reverse Polish Notation, Generate Parentheses, Longest Valid Parentheses and Daily Temperatures all use the same stack thinking.

Content Protection by DMCA.com
Spread the love
Scroll to Top
×