1609. Even Odd Tree
Problem Description
You are given the root of a binary tree. Your task is to determine if the tree is an "Even-Odd" tree.
A binary tree is considered an Even-Odd tree if it satisfies these specific conditions:
-
Level indexing: The root node is at level 0, its children are at level 1, their children are at level 2, and so on.
-
Even-indexed levels (level 0, 2, 4, ...):
- All node values must be odd integers
- Values must be in strictly increasing order when read from left to right
- For example, if nodes at level 0 have values
[3, 5, 7]
from left to right, this is valid
-
Odd-indexed levels (level 1, 3, 5, ...):
- All node values must be even integers
- Values must be in strictly decreasing order when read from left to right
- For example, if nodes at level 1 have values
[10, 8, 4]
from left to right, this is valid
The solution uses a level-order traversal (BFS) approach. It processes the tree level by level using a queue. For each level, it tracks whether it's an even or odd-indexed level using a flag variable even
. As it processes nodes at each level from left to right, it checks:
- Whether the node values have the correct parity (odd for even levels, even for odd levels)
- Whether the ordering constraint is satisfied (strictly increasing for even levels, strictly decreasing for odd levels)
The variable prev
keeps track of the previous node's value to verify the ordering. It's initialized to 0 for even levels (since all values must be positive and odd, any valid value will be greater than 0) and infinity for odd levels (since all values must be positive and even, any valid value will be less than infinity).
Return true
if the tree meets all the Even-Odd conditions, otherwise return false
.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: While this is a binary tree problem, trees are a special type of graph (specifically, a connected acyclic graph). The problem involves traversing nodes and their connections.
Is it a tree?
- Yes: The problem explicitly states we're working with a binary tree structure with a root node and parent-child relationships.
DFS?
- Not necessarily: While DFS could work here, the problem requires us to process nodes level by level. We need to validate that all nodes at each level satisfy certain conditions (odd/even values, strictly increasing/decreasing order). This level-by-level requirement strongly suggests we need to process all nodes at the same depth before moving to the next level.
Since the problem requires level-order traversal (processing all nodes at level 0, then all nodes at level 1, etc.) and validating properties across each level horizontally, BFS (Breadth-First Search) is the natural choice. BFS inherently processes nodes level by level using a queue, which perfectly aligns with our need to:
- Visit all nodes at the current level
- Check if values are strictly increasing/decreasing across the level
- Verify odd/even constraints for each level
- Move to the next level only after completing the current one
Conclusion: The flowchart leads us to BFS as the optimal approach for the Even-Odd Tree problem, as it naturally handles the level-by-level traversal and validation required by the problem constraints.
Intuition
When examining this problem, the key insight is that we need to validate conditions that span horizontally across each level of the tree. We must check that nodes at the same depth follow a specific ordering pattern (strictly increasing or decreasing) and have the correct parity (odd or even values).
Think about how we would verify these conditions manually: we'd look at all nodes at level 0, check if they're odd and in increasing order, then look at all nodes at level 1, check if they're even and in decreasing order, and so on. This natural thought process directly maps to a level-order traversal.
Why not DFS? With depth-first traversal, we'd jump between different levels as we explore the tree. We might visit a node at level 2, then backtrack to level 1, then dive down to level 3. This jumping makes it extremely difficult to track the ordering constraint - we'd need complex bookkeeping to remember what values we've seen at each level and in what order.
BFS, on the other hand, processes the tree level by level using a queue. All nodes at level 0 are processed before any node at level 1. This gives us a natural way to:
- Process nodes from left to right at each level (maintaining the order needed for comparison)
- Keep track of the previous value we've seen at the current level
- Switch our validation rules (odd/increasing vs even/decreasing) only when we complete a level
The implementation becomes straightforward: use a flag to track whether we're at an even or odd level, maintain a prev
variable to compare consecutive values at the same level, and flip our validation logic each time we complete processing all nodes at a level. The queue ensures we never mix nodes from different levels during validation.
Learn more about Tree, Breadth-First Search and Binary Tree patterns.
Solution Approach
The solution implements a level-order traversal using BFS with a queue data structure. Here's how the implementation works:
Initialization:
- Start with
even = 1
as a flag (1 for even-indexed levels, 0 for odd-indexed levels) - Create a queue
q
with the root node - The queue will maintain nodes to be processed at each level
Level-by-level Processing: The main loop continues while the queue has nodes:
-
Set up level validation:
- Initialize
prev
based on the current level type:- For even levels:
prev = 0
(since all valid values must be positive odd numbers > 0) - For odd levels:
prev = inf
(since all valid values must be positive even numbers < infinity)
- For even levels:
- Initialize
-
Process all nodes at current level:
- Use
for _ in range(len(q))
to process exactly the nodes at this level - For each node, perform validation checks:
For even-indexed levels (
even = 1
):- Check if
root.val % 2 == 0
(value must be odd, so this should be false) - Check if
prev >= root.val
(values must be strictly increasing) - If either condition is true, return
False
For odd-indexed levels (
even = 0
):- Check if
root.val % 2 == 1
(value must be even, so this should be false) - Check if
prev <= root.val
(values must be strictly decreasing) - If either condition is true, return
False
- Use
-
Update and continue:
- Update
prev = root.val
for the next comparison - Add the node's children to the queue (left child first, then right child)
- This maintains left-to-right order for the next level
- Update
-
Switch level type:
- After processing all nodes at the current level, toggle the level type using
even ^= 1
- This XOR operation flips between 1 (even-indexed) and 0 (odd-indexed)
- After processing all nodes at the current level, toggle the level type using
Time Complexity: O(n)
where n is the number of nodes, as we visit each node exactly once
Space Complexity: O(w)
where w is the maximum width of the tree, representing the maximum number of nodes in the queue at any time
The algorithm elegantly handles the alternating validation rules by using the even
flag and adjusting the comparison logic accordingly. The queue ensures proper level-order traversal, while the inner loop processes exactly one level at a time.
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 a small example to illustrate the solution approach.
Consider this binary tree:
5 (level 0 - even) / \ 4 2 (level 1 - odd) / \ \ 3 7 9 (level 2 - even)
Step 1: Initialize
even = 1
(starting at level 0, which is even-indexed)q = [5]
(queue contains root)
Step 2: Process Level 0 (even-indexed)
- Set
prev = 0
(for even levels, start with 0) - Process node 5:
- Is 5 even? No (✓)
- Is
prev >= 5
? Is0 >= 5
? No (✓) - Update
prev = 5
- Add children to queue:
q = [4, 2]
- Toggle level:
even = 0
(next level is odd-indexed)
Step 3: Process Level 1 (odd-indexed)
- Set
prev = inf
(for odd levels, start with infinity) - Process node 4:
- Is 4 odd? No (✓)
- Is
prev <= 4
? Isinf <= 4
? No (✓) - Update
prev = 4
- Add children to queue:
q = [2, 3, 7]
- Process node 2:
- Is 2 odd? No (✓)
- Is
prev <= 2
? Is4 <= 2
? No (✓) - Update
prev = 2
- Add children to queue:
q = [3, 7, 9]
- Toggle level:
even = 1
(next level is even-indexed)
Step 4: Process Level 2 (even-indexed)
- Set
prev = 0
(for even levels) - Process node 3:
- Is 3 even? No (✓)
- Is
prev >= 3
? Is0 >= 3
? No (✓) - Update
prev = 3
- Process node 7:
- Is 7 even? No (✓)
- Is
prev >= 7
? Is3 >= 7
? No (✓) - Update
prev = 7
- Process node 9:
- Is 9 even? No (✓)
- Is
prev >= 9
? Is7 >= 9
? No (✓) - Update
prev = 9
Result: The tree satisfies all conditions, so return true
.
The key insight is how the algorithm maintains the prev
variable within each level to check ordering, and resets it appropriately when moving to a new level. The even
flag elegantly switches the validation rules between levels.
Solution Implementation
1from collections import deque
2from typing import Optional
3from math import inf
4
5# Definition for a binary tree node.
6# class TreeNode:
7# def __init__(self, val=0, left=None, right=None):
8# self.val = val
9# self.left = left
10# self.right = right
11
12class Solution:
13 def isEvenOddTree(self, root: Optional[TreeNode]) -> bool:
14 """
15 Determines if a binary tree is an Even-Odd Tree.
16
17 An Even-Odd Tree satisfies:
18 - Nodes at even-indexed levels have odd values in strictly increasing order
19 - Nodes at odd-indexed levels have even values in strictly decreasing order
20
21 Args:
22 root: The root node of the binary tree
23
24 Returns:
25 True if the tree is an Even-Odd Tree, False otherwise
26 """
27 # Track if current level is even-indexed (starting from level 0)
28 is_even_level = True
29
30 # Initialize queue for level-order traversal
31 queue = deque([root])
32
33 # Process tree level by level
34 while queue:
35 # Initialize previous value based on level type
36 # For even levels: start with minimum to ensure first value is larger
37 # For odd levels: start with maximum to ensure first value is smaller
38 previous_value = 0 if is_even_level else inf
39
40 # Process all nodes at current level
41 level_size = len(queue)
42 for _ in range(level_size):
43 current_node = queue.popleft()
44
45 # Check conditions for even-indexed levels
46 if is_even_level:
47 # Values must be odd and strictly increasing
48 if current_node.val % 2 == 0 or previous_value >= current_node.val:
49 return False
50
51 # Check conditions for odd-indexed levels
52 else:
53 # Values must be even and strictly decreasing
54 if current_node.val % 2 == 1 or previous_value <= current_node.val:
55 return False
56
57 # Update previous value for next comparison
58 previous_value = current_node.val
59
60 # Add children to queue for next level
61 if current_node.left:
62 queue.append(current_node.left)
63 if current_node.right:
64 queue.append(current_node.right)
65
66 # Toggle level parity for next iteration
67 is_even_level = not is_even_level
68
69 # All levels satisfied the conditions
70 return True
71
1/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16class Solution {
17 public boolean isEvenOddTree(TreeNode root) {
18 // Track whether current level is even-indexed (0, 2, 4...) or odd-indexed (1, 3, 5...)
19 boolean isEvenLevel = true;
20
21 // Queue for level-order traversal (BFS)
22 Deque<TreeNode> queue = new ArrayDeque<>();
23 queue.offer(root);
24
25 // Process tree level by level
26 while (!queue.isEmpty()) {
27 // Initialize previous value based on level type
28 // For even levels: values must be strictly increasing, start with minimum
29 // For odd levels: values must be strictly decreasing, start with maximum
30 int previousValue = isEvenLevel ? 0 : 1000001;
31
32 // Process all nodes in current level
33 int levelSize = queue.size();
34 for (int i = 0; i < levelSize; i++) {
35 TreeNode currentNode = queue.pollFirst();
36
37 // Check even level constraints:
38 // - Values must be odd
39 // - Values must be strictly increasing
40 if (isEvenLevel) {
41 if (currentNode.val % 2 == 0 || previousValue >= currentNode.val) {
42 return false;
43 }
44 }
45
46 // Check odd level constraints:
47 // - Values must be even
48 // - Values must be strictly decreasing
49 if (!isEvenLevel) {
50 if (currentNode.val % 2 == 1 || previousValue <= currentNode.val) {
51 return false;
52 }
53 }
54
55 // Update previous value for next iteration
56 previousValue = currentNode.val;
57
58 // Add children to queue for next level processing
59 if (currentNode.left != null) {
60 queue.offer(currentNode.left);
61 }
62 if (currentNode.right != null) {
63 queue.offer(currentNode.right);
64 }
65 }
66
67 // Toggle level type for next iteration
68 isEvenLevel = !isEvenLevel;
69 }
70
71 // All levels satisfy the constraints
72 return true;
73 }
74}
75
1/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14 bool isEvenOddTree(TreeNode* root) {
15 // Track whether current level is even-indexed (0, 2, 4...) or odd-indexed (1, 3, 5...)
16 bool isEvenLevel = true;
17
18 // Queue for BFS traversal
19 queue<TreeNode*> nodeQueue;
20 nodeQueue.push(root);
21
22 // Process tree level by level
23 while (!nodeQueue.empty()) {
24 // Initialize previous value based on level type
25 // For even levels: values must be strictly increasing, start with minimum
26 // For odd levels: values must be strictly decreasing, start with maximum
27 int previousValue = isEvenLevel ? INT_MIN : INT_MAX;
28
29 // Process all nodes in current level
30 int levelSize = nodeQueue.size();
31 for (int i = 0; i < levelSize; ++i) {
32 TreeNode* currentNode = nodeQueue.front();
33 nodeQueue.pop();
34
35 // Check even-indexed level constraints:
36 // - Values must be odd
37 // - Values must be strictly increasing
38 if (isEvenLevel) {
39 if (currentNode->val % 2 == 0 || currentNode->val <= previousValue) {
40 return false;
41 }
42 }
43 // Check odd-indexed level constraints:
44 // - Values must be even
45 // - Values must be strictly decreasing
46 else {
47 if (currentNode->val % 2 == 1 || currentNode->val >= previousValue) {
48 return false;
49 }
50 }
51
52 // Update previous value for next comparison
53 previousValue = currentNode->val;
54
55 // Add children to queue for next level processing
56 if (currentNode->left) {
57 nodeQueue.push(currentNode->left);
58 }
59 if (currentNode->right) {
60 nodeQueue.push(currentNode->right);
61 }
62 }
63
64 // Toggle level type for next iteration
65 isEvenLevel = !isEvenLevel;
66 }
67
68 // All levels satisfy the constraints
69 return true;
70 }
71};
72
1/**
2 * Definition for a binary tree node.
3 */
4class TreeNode {
5 val: number;
6 left: TreeNode | null;
7 right: TreeNode | null;
8 constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
9 this.val = (val === undefined ? 0 : val);
10 this.left = (left === undefined ? null : left);
11 this.right = (right === undefined ? null : right);
12 }
13}
14
15/**
16 * Determines if a binary tree is an Even-Odd Tree.
17 * An Even-Odd Tree meets the following conditions:
18 * - The root of the tree is at level index 0
19 * - For every even-indexed level, all nodes have odd values in strictly increasing order (left to right)
20 * - For every odd-indexed level, all nodes have even values in strictly decreasing order (left to right)
21 *
22 * @param root - The root node of the binary tree
23 * @returns true if the tree is an Even-Odd Tree, false otherwise
24 */
25function isEvenOddTree(root: TreeNode | null): boolean {
26 // Handle edge case of empty tree
27 if (!root) {
28 return true;
29 }
30
31 // Track whether current level is even-indexed (0, 2, 4...) or odd-indexed (1, 3, 5...)
32 let isEvenLevel = true;
33
34 // Queue for BFS (Breadth-First Search) traversal
35 const nodeQueue: TreeNode[] = [];
36 nodeQueue.push(root);
37
38 // Process tree level by level
39 while (nodeQueue.length > 0) {
40 // Initialize previous value based on level type
41 // For even levels: values must be strictly increasing, start with minimum
42 // For odd levels: values must be strictly decreasing, start with maximum
43 let previousValue = isEvenLevel ? Number.MIN_SAFE_INTEGER : Number.MAX_SAFE_INTEGER;
44
45 // Process all nodes in current level
46 const levelSize = nodeQueue.length;
47 for (let i = 0; i < levelSize; i++) {
48 const currentNode = nodeQueue.shift()!;
49
50 // Check even-indexed level constraints:
51 // - Values must be odd
52 // - Values must be strictly increasing
53 if (isEvenLevel) {
54 if (currentNode.val % 2 === 0 || currentNode.val <= previousValue) {
55 return false;
56 }
57 }
58 // Check odd-indexed level constraints:
59 // - Values must be even
60 // - Values must be strictly decreasing
61 else {
62 if (currentNode.val % 2 === 1 || currentNode.val >= previousValue) {
63 return false;
64 }
65 }
66
67 // Update previous value for next comparison
68 previousValue = currentNode.val;
69
70 // Add children to queue for next level processing
71 if (currentNode.left !== null) {
72 nodeQueue.push(currentNode.left);
73 }
74 if (currentNode.right !== null) {
75 nodeQueue.push(currentNode.right);
76 }
77 }
78
79 // Toggle level type for next iteration
80 isEvenLevel = !isEvenLevel;
81 }
82
83 // All levels satisfy the constraints
84 return true;
85}
86
Time and Space Complexity
Time Complexity: O(n)
The algorithm uses BFS (Breadth-First Search) to traverse the binary tree level by level. Each node in the tree is visited exactly once - we dequeue it, process it (checking if it satisfies the even-odd conditions), and then enqueue its children if they exist. Since we visit each of the n
nodes exactly once and perform constant-time operations for each node (modulo operation, comparisons, and queue operations), the overall time complexity is O(n)
.
Space Complexity: O(n)
The space complexity is determined by the maximum size of the queue q
at any point during execution. In the worst case, the queue will contain all nodes at the widest level of the tree. For a complete binary tree, the maximum width occurs at the last level (or second-to-last for non-perfect trees), which can contain up to n/2
nodes. Additionally, we use a constant amount of extra space for variables like even
, prev
, and root
. Therefore, the space complexity is O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Initialization of Previous Value
A critical pitfall is incorrectly initializing the previous_value
variable for comparison. Many developers mistakenly use the same initialization value for both even and odd levels.
Incorrect approach:
# Wrong: Using same initial value for both level types previous_value = 0 # This won't work for odd levels!
Why it fails:
- For odd levels with strictly decreasing even values, if we initialize
previous_value = 0
, the first comparison0 <= current_node.val
will always be true (since node values are positive), causing incorrect rejection of valid trees.
Correct approach:
# Initialize based on level type
previous_value = 0 if is_even_level else float('inf')
2. Processing Nodes Across Multiple Levels in One Iteration
Another common mistake is not properly isolating level processing, leading to comparisons between nodes from different levels.
Incorrect approach:
while queue: node = queue.popleft() # Process node without tracking level boundaries # This mixes nodes from different levels!
Correct approach:
while queue:
level_size = len(queue) # Capture current level size
for _ in range(level_size): # Process exactly this many nodes
node = queue.popleft()
# Process node
3. Confusing Parity Check Logic
The condition checks can be counterintuitive because we're checking for failure conditions, not success conditions.
Common confusion:
# Incorrect: Checking for valid condition instead of invalid if is_even_level: if current_node.val % 2 == 1: # Wrong! This checks if it's odd # This would continue for valid odd values
Correct approach:
if is_even_level: if current_node.val % 2 == 0: # Check for INVALID condition (even when should be odd) return False
4. Using Weak Inequality Instead of Strict Inequality
The problem requires strictly increasing/decreasing order, not just non-decreasing/non-increasing.
Incorrect approach:
# Wrong: Allows equal consecutive values if previous_value > current_node.val: # Should be >= return False
Correct approach:
# For even levels (must be strictly increasing) if previous_value >= current_node.val: # Rejects equal values return False
5. Not Handling Edge Cases Properly
Failing to consider edge cases like single-node trees or trees with only one branch.
Solution: The BFS approach naturally handles these cases:
- Single node: The queue processes just the root, checks its value against level 0 requirements
- Unbalanced trees: The queue only adds existing children, naturally handling missing nodes
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Everything About Trees A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Binary Tree Min Depth Prereq BFS on Tree problems bfs_intro Given a binary tree find the depth of the shallowest leaf node https assets algo monster binary_tree_min_depth png Explanation We can solve this problem with either DFS or BFS With DFS we traverse the whole tree looking for leaf nodes and record and update the minimum depth as we go With BFS though since we search level by level we are guaranteed to find the shallowest leaf node
Want a Structured Path to Master System Design Too? Don’t Miss This!