1614. Maximum Nesting Depth of the Parentheses
Problem Description
You are given a valid parentheses string s
that may contain letters and parentheses characters (
and )
. Your task is to find the nesting depth of the string, which is the maximum number of nested parentheses at any point in the string.
For example:
- In the string
"(())"
, the maximum nesting depth is 2 (when we reach the innermost parentheses) - In the string
"(()(()))"
, the maximum nesting depth is 3 - In the string
"()()"
, the maximum nesting depth is 1
The solution works by tracking the current depth as we traverse the string. When we encounter an opening parenthesis (
, we increase the current depth by 1 and update our maximum depth if needed. When we encounter a closing parenthesis )
, we decrease the current depth by 1. Any other characters in the string are ignored.
The algorithm maintains:
- Variable
d
to track the current nesting level - Variable
ans
to store the maximum depth seen so far
As we scan through each character:
- If it's
(
: incrementd
and updateans = max(ans, d)
- If it's
)
: decrementd
- Otherwise: skip the character
Since the string is guaranteed to be valid (all parentheses are properly matched), the depth will always return to 0 by the end, and we return the maximum depth encountered during the traversal.
Intuition
Think of parentheses like entering and exiting nested rooms. Each opening parenthesis (
means we're entering a new room (going one level deeper), and each closing parenthesis )
means we're exiting a room (going back one level).
To find the maximum nesting depth, we need to track how deep we are at each moment and remember the deepest point we've reached.
Consider walking through the string "(()(()))"
:
- Start at depth 0
- See first
(
: enter room 1 (depth = 1) - See second
(
: enter room 2 (depth = 2) - See first
)
: exit back to room 1 (depth = 1) - See third
(
: enter another room 2 (depth = 2) - See fourth
(
: enter room 3 (depth = 3) - this is our deepest point! - See second
)
: exit to room 2 (depth = 2) - See third
)
: exit to room 1 (depth = 1) - See fourth
)
: exit to ground level (depth = 0)
The key insight is that we don't need to store the entire structure or use a stack. We just need a counter that goes up when we see (
and down when we see )
. The maximum value this counter reaches during our traversal is the answer.
This works because the string is guaranteed to be valid - every opening parenthesis has a matching closing parenthesis, so our depth counter will never go negative and will return to 0 by the end.
Learn more about Stack patterns.
Solution Approach
We use a simple traversal approach with a counter to track the current nesting depth.
Variables needed:
d
: Current depth counter, initialized to 0ans
: Maximum depth seen so far, initialized to 0
Algorithm:
-
Iterate through each character
c
in the strings
-
For each character:
- If
c == '('
:- Increment the depth counter:
d += 1
- Update the maximum depth:
ans = max(ans, d)
- Increment the depth counter:
- If
c == ')'
:- Decrement the depth counter:
d -= 1
- Decrement the depth counter:
- For any other character (letters, etc.), do nothing
- If
-
Return
ans
as the final answer
Why this works:
The algorithm maintains the invariant that d
always represents the current nesting level at any point during the traversal. Since we're guaranteed a valid parentheses string:
- The depth
d
will never become negative - The depth
d
will return to 0 after processing all characters - The maximum value of
d
during traversal is exactly the nesting depth we're looking for
Time and Space Complexity:
- Time:
O(n)
wheren
is the length of the string - we traverse the string once - Space:
O(1)
- we only use two integer variables regardless of input size
This greedy approach is optimal because we process each character exactly once and immediately update our answer, without needing to store any intermediate results or use additional data structures like a stack.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the string "(a(b)c)"
to demonstrate the solution approach:
Initial state:
d = 0
(current depth)ans = 0
(maximum depth seen)
Step-by-step traversal:
-
Character '(': Opening parenthesis
- Increment depth:
d = 1
- Update maximum:
ans = max(0, 1) = 1
- State:
d = 1, ans = 1
- Increment depth:
-
Character 'a': Letter (ignore)
- No change to depth
- State:
d = 1, ans = 1
-
Character '(': Opening parenthesis
- Increment depth:
d = 2
- Update maximum:
ans = max(1, 2) = 2
- State:
d = 2, ans = 2
- Increment depth:
-
Character 'b': Letter (ignore)
- No change to depth
- State:
d = 2, ans = 2
-
Character ')': Closing parenthesis
- Decrement depth:
d = 1
- State:
d = 1, ans = 2
- Decrement depth:
-
Character 'c': Letter (ignore)
- No change to depth
- State:
d = 1, ans = 2
-
Character ')': Closing parenthesis
- Decrement depth:
d = 0
- State:
d = 0, ans = 2
- Decrement depth:
Result: The maximum nesting depth is 2
The depth reached its peak of 2 when we were inside both sets of parentheses at character position 3. Notice how the depth counter correctly tracked our nesting level throughout, going up with each '(' and down with each ')', while ignoring the letters entirely.
Solution Implementation
1class Solution:
2 def maxDepth(self, s: str) -> int:
3 """
4 Calculate the maximum depth of nested parentheses in a string.
5
6 Args:
7 s: Input string containing parentheses and other characters
8
9 Returns:
10 Maximum nesting depth of valid parentheses
11 """
12 max_depth = 0 # Track the maximum depth encountered
13 current_depth = 0 # Track the current nesting level
14
15 # Iterate through each character in the string
16 for char in s:
17 if char == '(':
18 # Opening parenthesis increases depth
19 current_depth += 1
20 # Update maximum depth if current is greater
21 max_depth = max(max_depth, current_depth)
22 elif char == ')':
23 # Closing parenthesis decreases depth
24 current_depth -= 1
25 # Other characters are ignored
26
27 return max_depth
28
1class Solution {
2 /**
3 * Calculates the maximum depth of nested parentheses in a string.
4 * The depth is defined as the maximum number of nested parentheses at any point.
5 *
6 * @param s The input string containing parentheses and other characters
7 * @return The maximum nesting depth of parentheses
8 */
9 public int maxDepth(String s) {
10 int maxDepth = 0; // Tracks the maximum depth encountered
11 int currentDepth = 0; // Tracks the current nesting level
12
13 // Iterate through each character in the string
14 for (int i = 0; i < s.length(); i++) {
15 char currentChar = s.charAt(i);
16
17 // Opening parenthesis increases the current depth
18 if (currentChar == '(') {
19 currentDepth++;
20 // Update maximum depth if current depth exceeds it
21 maxDepth = Math.max(maxDepth, currentDepth);
22 }
23 // Closing parenthesis decreases the current depth
24 else if (currentChar == ')') {
25 currentDepth--;
26 }
27 // Other characters are ignored
28 }
29
30 return maxDepth;
31 }
32}
33
1class Solution {
2public:
3 int maxDepth(string s) {
4 int maxNestingDepth = 0; // Track the maximum nesting depth encountered
5 int currentDepth = 0; // Track the current nesting depth while traversing
6
7 // Iterate through each character in the string
8 for (char& ch : s) {
9 if (ch == '(') {
10 // Opening parenthesis increases the current depth
11 currentDepth++;
12 // Update maximum depth if current depth exceeds it
13 maxNestingDepth = max(maxNestingDepth, currentDepth);
14 } else if (ch == ')') {
15 // Closing parenthesis decreases the current depth
16 currentDepth--;
17 }
18 // Other characters are ignored (digits, operators, etc.)
19 }
20
21 return maxNestingDepth;
22 }
23};
24
1/**
2 * Calculates the maximum depth of nested parentheses in a string
3 * @param s - Input string containing parentheses and other characters
4 * @returns The maximum nesting depth of valid parentheses
5 */
6function maxDepth(s: string): number {
7 // Track the maximum depth encountered
8 let maxDepthFound: number = 0;
9
10 // Track the current depth while traversing
11 let currentDepth: number = 0;
12
13 // Iterate through each character in the string
14 for (const character of s) {
15 if (character === '(') {
16 // Opening parenthesis increases current depth
17 currentDepth++;
18 // Update maximum depth if current depth is greater
19 maxDepthFound = Math.max(maxDepthFound, currentDepth);
20 } else if (character === ')') {
21 // Closing parenthesis decreases current depth
22 currentDepth--;
23 }
24 // Other characters are ignored
25 }
26
27 return maxDepthFound;
28}
29
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the string s
. The algorithm iterates through each character in the string exactly once using a single for loop. Each operation inside the loop (comparison, increment/decrement, and max calculation) takes constant time O(1)
. Therefore, the total time complexity is O(n) * O(1) = O(n)
.
The space complexity is O(1)
. The algorithm only uses a fixed amount of extra space regardless of the input size. It maintains two integer variables: ans
to track the maximum depth encountered and d
to track the current depth. The loop variable c
also uses constant space. Since the space usage doesn't grow with the input size, the space complexity is constant O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Updating Maximum Depth at the Wrong Time
A common mistake is updating the maximum depth when encountering a closing parenthesis )
or after modifying the depth counter, rather than immediately after incrementing for an opening parenthesis (
.
Incorrect Implementation:
def maxDepth(self, s: str) -> int:
max_depth = 0
current_depth = 0
for char in s:
if char == '(':
current_depth += 1
elif char == ')':
current_depth -= 1
max_depth = max(max_depth, current_depth) # Wrong place!
return max_depth
This fails because when we encounter )
, we've already decreased the depth, missing the peak depth that occurred just before.
Correct Approach:
Update max_depth
immediately after incrementing current_depth
for opening parentheses.
2. Not Handling Empty Strings or Strings Without Parentheses
While the code handles these cases correctly (returning 0), developers might add unnecessary special case checks that complicate the code:
Overcomplicated:
def maxDepth(self, s: str) -> int:
if not s or '(' not in s: # Unnecessary checks
return 0
# ... rest of the code
The algorithm naturally handles these cases without special checks since the depth counters start at 0 and remain 0 if no parentheses are found.
3. Using a Stack When Not Necessary
Some developers might instinctively reach for a stack data structure since parentheses matching problems often use stacks:
Overly Complex:
def maxDepth(self, s: str) -> int:
stack = []
max_depth = 0
for char in s:
if char == '(':
stack.append(char)
max_depth = max(max_depth, len(stack))
elif char == ')':
stack.pop()
return max_depth
While this works, it uses O(n) space unnecessarily when a simple counter suffices for tracking depth.
4. Forgetting to Skip Non-Parenthesis Characters
Attempting to process letters or other characters as parentheses:
Incorrect:
def maxDepth(self, s: str) -> int:
max_depth = 0
current_depth = 0
for char in s:
if char == '(':
current_depth += 1
max_depth = max(max_depth, current_depth)
else: # Wrong! This treats all non-'(' as ')'
current_depth -= 1
return max_depth
This incorrectly decrements depth for letters and other characters, potentially making depth negative.
Solution: Use explicit checks for both (
and )
, ignoring all other characters.
What's the output of running the following function using the following tree as input?
1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!