Even Odd Tree
MedPrompt
A binary tree is named Even-Odd if it meets the following conditions:
- The root of the binary tree is at level index
0, its children are at level index1, their children are at level index2, etc. - For every even-indexed level, all nodes at the level have odd integer values in strictly increasing order (from left to right).
- For every odd-indexed level, all nodes at the level have even integer values in strictly decreasing order (from left to right).
Given the root of a binary tree, return true if the binary tree is Even-Odd, otherwise return false.
Example 1:
Input: root = [1,10,4,3,null,7,9,12,8,6,null,null,2]
Output: true
Explanation: The node values on each level are:
Level 0: [1]
Level 1: [10,4]
Level 2: [3,7,9]
Level 3: [12,8,6,2]
Since levels 0 and 2 are all odd and increasing and levels 1 and 3 are all even and decreasing, the tree is Even-Odd.Example 2:
Input: root = [5,4,2,3,3,7]
Output: false
Explanation: The node values on each level are:
Level 0: [5]
Level 1: [4,2]
Level 2: [3,3,7]
Node values in level 2 must be in strictly increasing order, so the tree is not Even-Odd.Example 3:
Input: root = [5,9,1,3,5,7]
Output: false
Explanation: Node values in the level 1 should be even integers.
Constraints:
- The number of nodes in the tree is in the range
[1, 105]. 1 <= Node.val <= 106
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses a recursive Depth-First Search (DFS) to traverse the tree. To enforce the ordering constraints (strictly increasing/decreasing) at each level, we need to keep track of the last value encountered at every level. A list can be used for this purpose, where the index corresponds to the tree level. A pre-order traversal ensures that nodes at a given level are processed from left to right.
Algorithm
- Use a recursive helper function
dfs(node, level). - Maintain a list,
lastValues, wherelastValues.get(level)stores the value of the previously visited node at thatlevel. - The traversal must be pre-order (
root,left,right) to ensure nodes at the same level are visited from left to right. - Base Case: If
nodeisnull, returntrue. - Parity Check:
- If
levelis even,node.valmust be odd. If not, returnfalse. - If
levelis odd,node.valmust be even. If not, returnfalse.
- If
- Order Check:
- If it's the first node at this
level(i.e.,level >= lastValues.size()), add its value tolastValues. - Otherwise, get
prevVal = lastValues.get(level). - For an even
level, check ifnode.val > prevVal. If not, returnfalse. - For an odd
level, check ifnode.val < prevVal. If not, returnfalse. - If the check passes, update
lastValues.set(level, node.val).
- If it's the first node at this
- Recursive Step: Return
trueonly if the recursive calls for both left and right children,dfs(node.left, level + 1)anddfs(node.right, level + 1), returntrue.
Walkthrough
We perform a pre-order traversal (root, left, right) of the tree. This ensures that for any given level, we visit the nodes from left to right.
A helper function, say dfs(node, level), is used. It takes the current node and its level as arguments.
We maintain a list, lastValues, accessible by the recursive calls, where lastValues.get(level) stores the value of the previously visited node at that level.
import java.util.ArrayList;import java.util.List; /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */class Solution { // List to store the last seen value at each level private List<Integer> lastValues = new ArrayList<>(); public boolean isEvenOddTree(TreeNode root) { return dfs(root, 0); } private boolean dfs(TreeNode node, int level) { if (node == null) { return true; } // Condition 1: Parity check based on level // If level is even, value must be odd. If level is odd, value must be even. // This is equivalent to checking if level and value have different parity. if (level % 2 == node.val % 2) { return false; } // Condition 2: Order check // If this is the first node we've seen at this level if (level >= lastValues.size()) { lastValues.add(node.val); } else { int prevVal = lastValues.get(level); // Even level: must be strictly increasing if (level % 2 == 0) { if (node.val <= prevVal) { return false; } } // Odd level: must be strictly decreasing else { if (node.val >= prevVal) { return false; } } // Update the last value for the current level lastValues.set(level, node.val); } // Recurse for children return dfs(node.left, level + 1) && dfs(node.right, level + 1); }}Complexity
Time
O(N), where N is the number of nodes in the tree. Each node is visited exactly once.
Space
O(H), where H is the height of the tree. This space is used by the recursion stack and the `lastValues` list. In the worst case of a skewed tree, H can be equal to N (the number of nodes), leading to O(N) space complexity.
Trade-offs
Pros
Conceptually straightforward implementation of tree traversal.
Cons
Can cause a
StackOverflowErrorfor very deep trees due to the depth of recursion.The space complexity depends on the tree's height, which can be O(N) for skewed trees, making it less memory-efficient in such cases compared to BFS.
This is the most natural and generally more robust approach for problems involving level-by-level processing of a tree. We traverse the tree one level at a time using a queue (Breadth-First Search), and at each level, we verify the Even-Odd tree conditions.
Algorithm
- Initialize a
Queueand add therootnode. - Initialize a
levelcounter to 0. - Loop while the queue is not empty.
- Get the number of nodes at the current level,
size = queue.size(). - Initialize
prevVal. For even levels, initialize toInteger.MIN_VALUE. For odd levels, initialize toInteger.MAX_VALUE. - Loop
sizetimes to process all nodes of the current level:- Dequeue a
node. - Even level: Check if
node.valis odd and strictly greater thanprevVal. If not, returnfalse. - Odd level: Check if
node.valis even and strictly less thanprevVal. If not, returnfalse. - Update
prevVal = node.val. - Enqueue the non-null left and right children of the
node.
- Dequeue a
- After processing the level, increment the
levelcounter.
- Get the number of nodes at the current level,
- If the main loop completes, return
true.
Walkthrough
We use a queue to perform a standard level-order traversal. A variable level keeps track of the current level index (starting from 0).
For each level, we process all nodes currently in the queue. We also keep track of the previous node's value (prevVal) within that level to check the ordering condition.
import java.util.LinkedList;import java.util.Queue; /** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */class Solution { public boolean isEvenOddTree(TreeNode root) { if (root == null) { return true; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); int level = 0; while (!queue.isEmpty()) { int size = queue.size(); // For even levels, we need increasing order, so prevVal starts small. // For odd levels, we need decreasing order, so prevVal starts large. int prevVal = (level % 2 == 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE; for (int i = 0; i < size; i++) { TreeNode node = queue.poll(); int val = node.val; if (level % 2 == 0) { // Even-indexed level // Value must be odd and strictly increasing. if (val % 2 == 0 || val <= prevVal) { return false; } } else { // Odd-indexed level // Value must be even and strictly decreasing. if (val % 2 != 0 || val >= prevVal) { return false; } } prevVal = val; if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } level++; } return true; }}Complexity
Time
O(N), where N is the number of nodes. Each node is enqueued and dequeued exactly once.
Space
O(W), where W is the maximum width of the tree. This is the maximum number of nodes that can be in the queue at any one time. In the worst case of a complete binary tree, the last level contains roughly N/2 nodes, making the space complexity O(N).
Trade-offs
Pros
Iterative approach avoids recursion and the risk of
StackOverflowErroron deep trees.Directly processes the tree level by level, which aligns perfectly with the problem statement.
Generally more space-efficient for deep, narrow trees compared to DFS.
Cons
Can be less space-efficient than DFS for very wide and shallow trees, as the queue might hold a large number of nodes.
Solutions
Solution
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public boolean isEvenOddTree ( TreeNode root ) { Queue < TreeNode > que = new LinkedList < > (); que . add ( root ); que . add ( null ); int res = 1 ; int c = 0 ; int prev = 0 ; while (! que . isEmpty ()) { if ( res % 2 == 0 ) prev = Integer . MAX_VALUE ; else prev = Integer . MIN_VALUE ; while ( que . peek () != null ) { if ( que . peek (). val % 2 != res % 2 ) { return false ; } if ( res % 2 == 0 && prev <= que . peek (). val ) { return false ; } if ( res % 2 != 0 && prev >= que . peek (). val ) return false ; if ( que . peek (). left != null ) que . add ( que . peek (). left ); if ( que . peek (). right != null ) que . add ( que . peek (). right ); prev = que . poll (). val ; } que . poll (); if ( que . isEmpty ()) { break ; } res ++; que . add ( null ); } return true ; } }/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: bool isEvenOddTree ( TreeNode * root ) { int even = 1 ; queue < TreeNode *> q { { root } }; while ( ! q . empty ()) { int prev = even ? 0 : 1e6 ; for ( int n = q . size (); n ; -- n ) { root = q . front (); q . pop (); if ( even && ( root -> val % 2 == 0 || prev >= root -> val )) return false ; if ( ! even && ( root -> val % 2 == 1 || prev <= root -> val )) return false ; prev = root -> val ; if ( root -> left ) q . push ( root -> left ); if ( root -> right ) q . push ( root -> right ); } even ^= 1 ; } return true ; } };# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution : def isEvenOddTree ( self , root : Optional [ TreeNode ]) -> bool : even = 1 q = deque ([ root ]) while q : prev = 0 if even else inf for _ in range ( len ( q )): root = q . popleft () if even and ( root . val % 2 == 0 or prev >= root . val ): return False if not even and ( root . val % 2 == 1 or prev <= root . val ): return False prev = root . val if root . left : q . append ( root . left ) if root . right : q . append ( root . right ) even ^= 1 return TrueVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.