Facebook Pixel

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:

  1. Level indexing: The root node is at level 0, its children are at level 1, their children are at level 2, and so on.

  2. 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
  3. 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:

  1. Visit all nodes at the current level
  2. Check if values are strictly increasing/decreasing across the level
  3. Verify odd/even constraints for each level
  4. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Process nodes from left to right at each level (maintaining the order needed for comparison)
  2. Keep track of the previous value we've seen at the current level
  3. 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:

  1. 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)
  2. 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
  3. 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
  4. 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)

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 Evaluator

Example 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? Is 0 >= 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? Is inf <= 4? No (✓)
    • Update prev = 4
    • Add children to queue: q = [2, 3, 7]
  • Process node 2:
    • Is 2 odd? No (✓)
    • Is prev <= 2? Is 4 <= 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? Is 0 >= 3? No (✓)
    • Update prev = 3
  • Process node 7:
    • Is 7 even? No (✓)
    • Is prev >= 7? Is 3 >= 7? No (✓)
    • Update prev = 7
  • Process node 9:
    • Is 9 even? No (✓)
    • Is prev >= 9? Is 7 >= 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 comparison 0 <= 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
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of these properties could exist for a graph but not a tree?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More