Problem#
You are given the root
of a binary tree.
A ZigZag path for a binary tree is defined as follow:
Choose any node in the binary tree and a direction (right or left).
If the current direction is right, move to the right child of the current node; otherwise, move to the left child.
Change the direction from right to left or from left to right.
Repeat the second and third steps until you can’t move in the tree.
Zigzag length is defined as the number of nodes visited - 1. (A single node has a length of 0).
Return the longest ZigZag path contained in that tree .
Examples#
Example 1:
1
2
3
4
5
Input:
root = [1,null,1,1,1,null,null,1,1,null,1,null,null,null,1,null,1]
Output:
3
Explanation: Longest ZigZag path in blue nodes (right -> left -> right).
Example 2:
1
2
3
4
5
Input:
root = [1,1,1,null,1,null,null,1,1,null,1]
Output:
4
Explanation: Longest ZigZag path in blue nodes (left -> right -> left -> right).
Example 3:
1
2
3
4
Input:
root = [1]
Output:
0
Solution#
Method 1 - DFS with Recursion#
Approach#
We use depth-first search (DFS) to explore all possible ZigZag paths starting from every node. At each node, we recursively try both directions (left and right), switching direction at each step. We keep track of the current path length and update the global maximum whenever a longer ZigZag is found.
Recurrence Relation#
Let dfs(node, direction, length)
be the function that returns the longest ZigZag path starting from node
, moving in direction
(left
or right
), with current path length length
.
If node
is null, return.
If direction
is left, next call is dfs(node.left, right, length + 1)
and restart from node.right
with length 1.
If direction
is right, next call is dfs(node.right, left, length + 1)
and restart from node.left
with length 1.
Base Case#
If the current node is null, the recursion stops.
Code#
Cpp
Go
Java
Kotlin
Python
Rust
Typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public :
int maxZigZag = 0 ;
void dfs (TreeNode* node, bool left, int length) {
if (! node) return ;
maxZigZag = std:: max(maxZigZag, length);
if (left) {
dfs(node-> left, false, length + 1 );
dfs(node-> right, true, 1 );
} else {
dfs(node-> right, true, length + 1 );
dfs(node-> left, false, 1 );
}
}
int longestZigZag (TreeNode* root) {
dfs(root, true, 0 );
dfs(root, false, 0 );
return maxZigZag;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func longestZigZag (root * TreeNode ) int {
var maxZigZag int
var dfs func (node * TreeNode , left bool , length int )
dfs = func (node * TreeNode , left bool , length int ) {
if node == nil {
return
}
if length > maxZigZag {
maxZigZag = length
}
if left {
dfs (node .Left , false , length + 1 )
dfs (node .Right , true , 1 )
} else {
dfs (node .Right , true , length + 1 )
dfs (node .Left , false , 1 )
}
}
dfs (root , true , 0 )
dfs (root , false , 0 )
return maxZigZag
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
int maxZigZag = 0;
public int longestZigZag (TreeNode root) {
dfs(root, true , 0);
dfs(root, false , 0);
return maxZigZag;
}
private void dfs (TreeNode node, boolean left, int length) {
if (node == null ) return ;
maxZigZag = Math.max (maxZigZag, length);
if (left) {
dfs(node.left , false , length + 1);
dfs(node.right , true , 1);
} else {
dfs(node.right , true , length + 1);
dfs(node.left , false , 1);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
var maxZigZag = 0
fun longestZigZag (root: TreeNode?): Int {
dfs(root, true , 0 )
dfs(root, false , 0 )
return maxZigZag
}
private fun dfs (node: TreeNode?, left: Boolean, length: Int) {
if (node == null ) return
maxZigZag = maxOf(maxZigZag, length)
if (left) {
dfs(node.left, false , length + 1 )
dfs(node.right, true , 1 )
} else {
dfs(node.right, true , length + 1 )
dfs(node.left, false , 1 )
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution :
def longestZigZag (self, root: Optional[TreeNode]) -> int:
self. maxZigZag = 0
def dfs (node, left, length):
if not node:
return
self. maxZigZag = max(self. maxZigZag, length)
if left:
dfs(node. left, False , length + 1 )
dfs(node. right, True , 1 )
else :
dfs(node. right, True , length + 1 )
dfs(node. left, False , 1 )
dfs(root, True , 0 )
dfs(root, False , 0 )
return self. maxZigZag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
impl Solution {
pub fn longest_zig_zag (root: Option< Rc< RefCell< TreeNode>>> ) -> i32 {
fn dfs (node: Option< Rc< RefCell< TreeNode>>> , left: bool , length: i32 , max_zigzag: & mut i32 ) {
if let Some(n) = node {
* max_zigzag = (* max_zigzag).max(length);
let n = n.borrow();
if left {
dfs(n.left.clone(), false , length + 1 , max_zigzag);
dfs(n.right.clone(), true , 1 , max_zigzag);
} else {
dfs(n.right.clone(), true , length + 1 , max_zigzag);
dfs(n.left.clone(), false , 1 , max_zigzag);
}
}
}
let mut max_zigzag = 0 ;
dfs(root.clone(), true , 0 , & mut max_zigzag);
dfs(root, false , 0 , & mut max_zigzag);
max_zigzag
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
maxZigZag = 0 ;
longestZigZag (root : TreeNode | null ): number {
this .dfs (root , true , 0 );
this .dfs (root , false , 0 );
return this .maxZigZag ;
}
private dfs (node : TreeNode | null , left : boolean , length : number ): void {
if (! node ) return ;
this .maxZigZag = Math.max (this .maxZigZag , length );
if (left ) {
this .dfs (node .left , false , length + 1 );
this .dfs (node .right , true , 1 );
} else {
this .dfs (node .right , true , length + 1 );
this .dfs (node .left , false , 1 );
}
}
}
Complexity#
⏰ Time complexity: O(N)
, because each node in the tree is visited a constant number of times by the DFS traversal.
🧺 Space complexity: O(H)
, where H
is the height of the tree, due to the recursion stack. In the worst case (a skewed tree), H = N
.