Longest ZigZag Path in a Binary Tree
Longest ZigZag Path in a Binary Tree: a zigzag path alternates direction at every step. Return the length of the longest one, measured in edges.
- The number of nodes is in the range [1, 5 * 10⁴].
- 1 <= Node.val <= 100
Intuition
Longest zigzag path in a binary tree finds the longest path that alternates direction at every step — left, right, left, and so on. The path length is counted in edges, so a single node has length 0. What distinguishes this from ordinary tree recursion is that the answer depends on how a node was reached. A node arrived at from the left can only extend by going right; the same node reached from the right extends by going left. So the recursion carries direction: - At each node, track the current direction and the length of the zigzag arriving there — two separate values, one per direction. Moving in the opposite direction to the one just travelled extends the path by one. Moving in the same direction restarts it at length 1, since the zigzag is broken but a new path begins at that step. That restart is the detail most often missed. Returning 0 on a direction change loses the fresh path starting at the current node, and the answer comes out short on trees where the longest zigzag begins mid-branch. As with Diameter of Binary Tree, the maximum is recorded on the side rather than returned. The recursion must return per-direction lengths so the parent can extend them, while the best answer overall lives in a variable updated at each node. The answer counts edges, not nodes, so a two-node path has length 1. Off-by-one errors here are common and only show on small trees. Every node is visited once with constant work, giving O(n) time and O(h) stack space.
When a path property depends on how you arrived, push that arrival state down as a DFS parameter rather than trying to reconstruct it. Direction-plus-length is the reusable shape — the same technique handles Longest Univalue Path and Binary Tree Maximum Path Sum, which carry different state for the same reason.
Approach
Before reading on: standing at a node, what two pieces of information do you need in order to know whether stepping left extends the zigzag or restarts it? Aim for O(n).
Carry the direction into the recursion
A node's contribution depends on how it was reached — arriving from the left means extending rightwards. The direction must travel with the recursion.
Track two lengths per node
Keep the zigzag length arriving from the left and from the right separately. One value cannot describe both continuations.
Extend on a direction change
Moving opposite to the previous step lengthens the path by one. This is the only way a zigzag grows.
Restart at one on a repeat
Moving the same direction breaks the zigzag but starts a new path of length 1 at that step. Returning 0 instead loses paths beginning mid-branch.
Record the maximum separately
Like Diameter of Binary Tree, the answer is held outside the recursion while the return value serves the parent. Mixing the two loses one or the other.
Count edges, not nodes
A single node has length 0 and a two-node path has length 1. Off-by-one errors here surface only on small trees.
Cost of the traversal
Each node is visited once with O(1) work, giving O(n) time and O(h) space for the recursion stack.
Solution & live demo
Common pitfalls
Counting nodes instead of edges
dfs(root, True, 1)
dfs(root, True, 0)
Length is measured in edges, so a lone node scores 0. Seeding with 1 inflates every answer by exactly one, which passes no test case.
Not restarting on a same-direction move
dfs(node.left, True, length + 1) # when arriving from the left
dfs(node.left, True, 1)
Moving the same way twice breaks the alternation, so the run ends and a new one begins at that node. Extending it instead reports straight chains as valid zigzags.
Only searching from the root
dfs(root, True, 0) return self.best
dfs(root, True, 0) dfs(root, False, 0)
The first call fixes an arrival direction at the root, exploring only half the possibilities. Both initial directions must be seeded, or paths beginning with the other move are never considered.
Edge cases
No edges exist, so the answer is 0.
Every step alternates and the length equals the depth minus one.
The direction never alternates, so each step restarts at 1 and the answer is 1.
The restart case seeds a run of 1 at every node, so interior starts are covered.
Recursion depth reaches O(n); on very deep trees an iterative stack avoids overflow.