1. How would you validate a string of parentheses?
Validate a string containing brackets and determine whether the nesting and order are correct.
I would use a stack to keep unmatched opening brackets. I process the string from left to right. When I see '(', '[', or '{', I push it onto the stack. For a closing bracket, I first check that the stack is not empty and that its top has the matching opening bracket. If not, I return false immediately. At the end, the stack must be empty. This runs in O(n) expected time and uses O(n) auxiliary space.
See the Code while reading this explanation.
The input is a string made only of parentheses, square brackets, and curly brackets. We need to return true only when every opening bracket has the correct closing bracket and the nesting order is valid. A stack fits this problem because the most recently opened bracket must be the first one closed. We push opening brackets onto the stack. For every closing bracket, we compare it with the current stack top. A mismatch makes the string invalid immediately. An empty stack at the end means every bracket was matched.
- Should an empty string be considered valid?
- Can I assume the input contains only '(', ')', '[', ']', '{', and '}'?
The input is a string of bracket characters. We return a boolean value. We return true only when every opening bracket is closed by the same bracket type and in the correct nesting order. For example, "{[()]}" is valid, so the expected result is true.
I use a stack to store opening brackets that have not been matched yet. The stack top is important because it represents the most recent unmatched opening bracket. I also use the matching table ) -> (, ] -> [, and } -> {. The central rule is that a closing bracket must match the current stack top.
The stack starts empty: []. Traversal starts at index 0. The invariant is that the stack contains exactly the unmatched opening brackets seen so far, in nesting order. The top is the only opening bracket that can legally match the next closing bracket.
For s = "{[()]}", index 0 contains '{'. The stack is empty, so we push '{'. The stack becomes ['{'] and processing continues.
At index 1, the character is '['. It is an opening bracket, so we push it. The stack changes from ['{'] to ['{','['].
At index 2, the character is '('. We push it. The stack changes from ['{','['] to ['{','[','('].
At index 3, the character is ')'. Before the check, the stack is ['{','[','(']. The matching table says ')' needs '('. The stack top is '(', so the pair is valid. We pop '('. The stack becomes ['{','['] and processing continues.
At index 4, the character is ']'. The stack is ['{','[']. The matching table says ']' needs '['. The top is '[', so we pop it. The stack becomes ['{'].
At index 5, the character is '}'. The stack is ['{']. The required opening bracket is '{'. It matches the top, so we pop it. The stack becomes [].
After all six characters are processed, stack.isEmpty() is true. We return true.
The stack always contains only unmatched opening brackets. They stay in the exact order in which they must later be closed. A closing bracket is valid only when it matches the current top. Popping a matching pair keeps the invariant true. If the stack is empty at the end, every opening bracket was matched in the correct order.
The Java code uses Deque<Character> with ArrayDeque as the stack. A Map<Character, Character> stores the required opening bracket for each closing bracket. Opening brackets are pushed with stack.push(). For a closing bracket, the code first checks stack.isEmpty(). If the stack is empty, it returns false immediately. Otherwise, it pops the top opening bracket and compares it with the required opening bracket from the map. A mismatch also returns false immediately. After the loop, it returns stack.isEmpty().
We process each character at most once and can stop early when the answer is already false. Stack push and pop are constant-time operations. The matching table contains only the three fixed closing-bracket mappings, so each lookup is constant time for this solution. The overall time is O(n), matching the diagram's expected O(n) bound. In the worst case, the stack can hold n opening brackets, so auxiliary space is O(n). Important cases are an empty string, a string starting with a closing bracket, mismatched nesting such as "([)]", and leftover opening brackets such as "(((".
The key insight is that valid brackets must close in last-in, first-out order. That is exactly how a stack works. Every opening bracket is pushed onto the stack. When a closing bracket appears, it must match the opening bracket at the top. If the stack is empty or the types do not match, the string is invalid immediately. The invariant is: the stack contains exactly the unmatched opening brackets seen so far, in nesting order. If the stack is empty after the full string is processed, every pair was matched correctly.
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Map;
public class Main {
public static boolean isValid(String s) {
// Store opening brackets that have not been matched yet.
Deque<Character> stack = new ArrayDeque<>();
// Map each closing bracket to the opening bracket it must match.
Map<Character, Character> matching = Map.of(')', '(', ']', '[', '}', '{');
// Process the input from left to right at most once.
for (char ch : s.toCharArray()) {
// Opening brackets wait on the stack for their closing bracket.
if (ch == '(' || ch == '[' || ch == '{') {
stack.push(ch);
} else {
// A closing bracket is invalid if no opening bracket is waiting.
if (stack.isEmpty()) {
return false;
}
// Remove the most recent unmatched opening bracket.
char opening = stack.pop();
// Its type must match the current closing bracket.
if (opening != matching.get(ch)) {
return false;
}
}
}
// The string is valid only when no opening brackets remain unmatched.
return stack.isEmpty();
}
public static void main(String[] args) {
// Run the same verified example shown in the diagram.
String s = "{[()]}";
System.out.println(isValid(s)); // true
}
}Let n be the number of characters in the string. We process each character at most once and may stop early on a mismatch, so the overall time is O(n), consistent with the diagram's expected O(n) bound. Stack push and pop are O(1). The matching table has only three fixed entries, so its lookup is constant time here. Auxiliary space means extra memory used by the algorithm. In the worst case, every character can be an opening bracket, so the stack can grow to n entries. The auxiliary space is O(n).
This stack pattern is useful in parsers, compilers, code editors, template validators, and configuration-file checks. It is especially useful when nested items must close in the reverse order in which they were opened, such as brackets, nested expressions, and other last-in, first-out structures.
This problem checks whether you recognize a last-in, first-out pattern and choose a stack naturally. It also tests whether you can maintain a clear invariant while processing a string, handle invalid states early, and distinguish bracket type from bracket count. In Java, the interviewer can also see whether you know how to use Deque and ArrayDeque correctly. Finally, it tests whether you can explain O(n) time, O(n) auxiliary space, and important edge cases.
A common mistake is checking only the number of opening and closing brackets. Equal counts do not prove that the nesting order is correct. Another mistake is popping without first checking whether the stack is empty. Candidates also sometimes forget to compare the closing bracket with the exact bracket type at the stack top. Another mistake is returning true without checking whether opening brackets remain in the stack. Finally, the push and pop order must stay last-in, first-out.
State the stack invariant before coding: the stack contains the unmatched opening brackets, and its top is the only bracket that may match the next closing bracket. That one sentence makes the push, pop, mismatch, and final empty-stack checks easy to justify.









