2415. Reverse Odd Levels of Binary Tree
Problem Description
You are given the root of a perfect binary tree. Your task is to reverse the node values at each odd level of the tree.
A perfect binary tree is a special type of binary tree where:
- Every parent node has exactly two children
- All leaf nodes are at the same depth/level
The level of a node is determined by counting the edges from the root to that node. The root is at level 0, its children are at level 1, and so on.
For odd-numbered levels (1, 3, 5, ...), you need to reverse the values of all nodes at that level horizontally. For example, if level 3 has nodes with values [2,1,3,4,7,11,29,18]
from left to right, after reversing they should become [18,29,11,7,4,3,1,2]
.
The solution uses a level-order traversal (BFS) approach:
- Use a queue to process nodes level by level
- Track the current level with a counter
i
- When
i
is odd (checked usingi & 1
), swap the values of nodes at that level- Use two pointers from the start and end of the current level's nodes
- Swap values moving the pointers toward each other
- Add all children of the current level to the queue for the next iteration
- Return the modified tree's root
The key insight is that we only need to swap the values of the nodes, not the actual node structures or their connections, which makes the problem simpler to solve.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: A binary tree is a special type of graph where each node has at most two children and there are no cycles.
Is it a tree?
- Yes: The problem explicitly states we're working with a binary tree (specifically a perfect binary tree).
DFS
- Yes: We arrive at DFS as the suggested approach for tree problems.
Conclusion: The flowchart suggests using a DFS (Depth-First Search) approach for this tree problem.
How DFS Applies to This Problem
While the provided solution uses BFS (level-order traversal), DFS is equally valid and can solve this problem effectively:
-
DFS with Level Tracking: We can traverse the tree using DFS while keeping track of the current level. During traversal, we collect nodes at each odd level.
-
Two-Pass DFS Approach:
- First pass: Collect all nodes at odd levels into lists
- Second pass: Traverse again and update values using the reversed lists
-
Recursive DFS Solution: We can recursively traverse pairs of nodes at odd levels and swap their values directly:
def dfs(left, right, level): if not left: return if level % 2 == 1: # odd level left.val, right.val = right.val, left.val dfs(left.left, right.right, level + 1) dfs(left.right, right.left, level + 1)
The flowchart correctly identifies this as a tree problem where DFS is applicable, though the specific implementation can vary between DFS and BFS based on what's more convenient for level-wise operations.
Intuition
When we need to reverse values at specific levels of a tree, the key insight is that we need to process nodes level by level. This immediately suggests a level-order traversal pattern.
Think about what "reversing at a level" means - if we have nodes [A, B, C, D]
at a level, we want their values to become [D, C, B, A]
. The crucial observation is that we don't need to change the tree structure itself - we only need to swap the values stored in the nodes.
Since it's a perfect binary tree, every level (except leaves) has all nodes present, making our task simpler. At any odd level, we know exactly how many nodes exist: 2^level
nodes.
For the swapping strategy, we can use a two-pointer approach:
- Collect all nodes at the current level
- If it's an odd level, place pointers at the start and end
- Swap values and move pointers toward each other until they meet
Why does this work better than DFS for this specific problem? While DFS can traverse the tree, BFS naturally groups nodes by level, which is exactly what we need. With BFS, we process all nodes at level 1, then all at level 2, and so on. This makes it trivial to identify when we're at an odd level (level % 2 == 1
) and perform the reversal operation on exactly those nodes.
The elegance of the solution comes from recognizing that:
- We need level-wise access to nodes
- We only swap values, not node structures
- A queue naturally maintains nodes in level order during BFS
- The two-pointer technique efficiently reverses values in-place
Learn more about Tree, Depth-First Search, Breadth-First Search and Binary Tree patterns.
Solution Approach
The solution uses Breadth-First Search (BFS) with a queue to traverse the tree level by level. Here's the step-by-step implementation:
1. Initialize the BFS Setup
- Create a queue
q
usingdeque([root])
to store nodes at each level - Initialize a level counter
i = 0
to track which level we're currently processing
2. Process Each Level While the queue is not empty:
- Check if current level
i
is odd using bitwise operationi & 1
- This is equivalent to
i % 2 == 1
but more efficient
- This is equivalent to
3. Reverse Values at Odd Levels If we're at an odd level:
- Set two pointers:
l = 0
(left) andr = len(q) - 1
(right) - While
l < r
:- Swap values:
q[l].val, q[r].val = q[r].val, q[l].val
- Move pointers inward:
l += 1
,r -= 1
- Swap values:
- This reverses all node values at the current level in-place
4. Add Next Level's Nodes Process all nodes at the current level:
- For each node in the current level (using
range(len(q))
):- Remove node from front:
node = q.popleft()
- If the node has children (guaranteed in perfect binary tree except for leaves):
- Add left child:
q.append(node.left)
- Add right child:
q.append(node.right)
- Add left child:
- Remove node from front:
5. Move to Next Level
- Increment level counter:
i += 1
- Continue until all levels are processed
6. Return Result
- Return the original
root
with modified values
The algorithm maintains the tree structure intact while only modifying the values at odd levels. The time complexity is O(n)
where n
is the number of nodes, as we visit each node exactly once. The space complexity is O(w)
where w
is the maximum width of the tree (at most n/2
for a perfect binary tree).
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small perfect binary tree example to illustrate the solution approach:
Initial Tree:
1 (level 0) / \ 2 3 (level 1 - odd) / \ / \ 4 5 6 7 (level 2)
Step 1: Initialize
- Queue:
[1]
- Level counter:
i = 0
Step 2: Process Level 0 (Root)
- Current level is 0 (even), so no reversal needed
- Process node 1:
- Remove 1 from queue
- Add its children 2 and 3 to queue
- Queue after level 0:
[2, 3]
- Increment level:
i = 1
Step 3: Process Level 1 (Odd - Reverse!)
- Current level is 1 (odd), so we need to reverse
- Queue contains:
[2, 3]
- Set pointers:
l = 0
,r = 1
- Swap values:
q[0].val
(2) ↔q[1].val
(3)- Now nodes have values:
[3, 2]
- Move pointers:
l = 1
,r = 0
(l >= r, stop) - Process both nodes:
- Remove node with value 3 (originally 2), add children 4, 5
- Remove node with value 2 (originally 3), add children 6, 7
- Queue after level 1:
[4, 5, 6, 7]
- Increment level:
i = 2
Step 4: Process Level 2 (Even)
- Current level is 2 (even), so no reversal needed
- Process all 4 nodes:
- They're leaf nodes, so no children to add
- Queue becomes empty
- Algorithm terminates
Final Tree:
1 (level 0 - unchanged) / \ 3 2 (level 1 - reversed!) / \ / \ 4 5 6 7 (level 2 - unchanged)
The values at level 1 have been successfully reversed from [2, 3]
to [3, 2]
, while maintaining the tree structure. Note that nodes 2 and 3 still have the same children as before - only their values were swapped, not their positions in the tree.
Solution Implementation
1# Definition for a binary tree node.
2# class TreeNode:
3# def __init__(self, val=0, left=None, right=None):
4# self.val = val
5# self.left = left
6# self.right = right
7
8from collections import deque
9from typing import Optional
10
11class Solution:
12 def reverseOddLevels(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
13 """
14 Reverses the values of nodes at odd levels in a perfect binary tree.
15 Uses BFS to traverse the tree level by level.
16
17 Args:
18 root: Root node of the perfect binary tree
19
20 Returns:
21 Root of the modified tree with odd level values reversed
22 """
23 # Initialize queue for BFS traversal
24 queue = deque([root])
25 level = 0
26
27 while queue:
28 # Check if current level is odd (using bitwise AND for efficiency)
29 if level & 1: # Equivalent to: level % 2 == 1
30 # Reverse values at odd levels using two-pointer approach
31 left_idx = 0
32 right_idx = len(queue) - 1
33
34 while left_idx < right_idx:
35 # Swap values of nodes at symmetric positions
36 queue[left_idx].val, queue[right_idx].val = queue[right_idx].val, queue[left_idx].val
37 left_idx += 1
38 right_idx -= 1
39
40 # Process all nodes at current level and add their children
41 level_size = len(queue)
42 for _ in range(level_size):
43 node = queue.popleft()
44
45 # Add children to queue for next level processing
46 # Since it's a perfect binary tree, if left exists, right also exists
47 if node.left:
48 queue.append(node.left)
49 queue.append(node.right)
50
51 # Move to next level
52 level += 1
53
54 return root
55
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 /**
18 * Reverses the node values at odd levels of a perfect binary tree.
19 * Uses BFS to traverse the tree level by level and reverses values at odd levels.
20 *
21 * @param root The root of the perfect binary tree
22 * @return The root of the modified tree
23 */
24 public TreeNode reverseOddLevels(TreeNode root) {
25 // Initialize queue for level-order traversal
26 Deque<TreeNode> queue = new ArrayDeque<>();
27 queue.offer(root);
28
29 // Process each level of the tree
30 int level = 0;
31 while (!queue.isEmpty()) {
32 // Store nodes at odd levels for value reversal
33 List<TreeNode> nodesAtOddLevel = new ArrayList<>();
34
35 // Process all nodes at current level
36 int levelSize = queue.size();
37 for (int i = 0; i < levelSize; i++) {
38 TreeNode currentNode = queue.poll();
39
40 // Collect nodes at odd levels
41 if (level % 2 == 1) {
42 nodesAtOddLevel.add(currentNode);
43 }
44
45 // Add children to queue for next level processing
46 if (currentNode.left != null) {
47 queue.offer(currentNode.left);
48 queue.offer(currentNode.right);
49 }
50 }
51
52 // Reverse values of nodes at odd levels using two-pointer approach
53 int left = 0;
54 int right = nodesAtOddLevel.size() - 1;
55 while (left < right) {
56 // Swap values between nodes at opposite ends
57 int tempValue = nodesAtOddLevel.get(left).val;
58 nodesAtOddLevel.get(left).val = nodesAtOddLevel.get(right).val;
59 nodesAtOddLevel.get(right).val = tempValue;
60
61 left++;
62 right--;
63 }
64
65 level++;
66 }
67
68 return root;
69 }
70}
71
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 TreeNode* reverseOddLevels(TreeNode* root) {
15 // Initialize queue for level-order traversal
16 queue<TreeNode*> nodeQueue;
17 nodeQueue.push(root);
18
19 // Process each level of the tree
20 // Level index starts from 0 (root is at level 0)
21 for (int levelIndex = 0; !nodeQueue.empty(); ++levelIndex) {
22 // Store nodes of odd levels for value reversal
23 vector<TreeNode*> oddLevelNodes;
24
25 // Process all nodes at the current level
26 int currentLevelSize = nodeQueue.size();
27 for (int i = 0; i < currentLevelSize; ++i) {
28 TreeNode* currentNode = nodeQueue.front();
29 nodeQueue.pop();
30
31 // If current level is odd, collect the node for reversal
32 if (levelIndex & 1) { // Bitwise AND to check if level is odd
33 oddLevelNodes.push_back(currentNode);
34 }
35
36 // Add children to queue for next level processing
37 if (currentNode->left) {
38 nodeQueue.push(currentNode->left);
39 nodeQueue.push(currentNode->right); // Perfect binary tree, so right child exists
40 }
41 }
42
43 // Reverse values of nodes at odd levels using two-pointer technique
44 int left = 0;
45 int right = oddLevelNodes.size() - 1;
46 while (left < right) {
47 swap(oddLevelNodes[left]->val, oddLevelNodes[right]->val);
48 ++left;
49 --right;
50 }
51 }
52
53 return root;
54 }
55};
56
1/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15/**
16 * Reverses the values of nodes at odd levels in a perfect binary tree
17 * Uses level-order traversal (BFS) to process nodes level by level
18 * @param root - The root node of the binary tree
19 * @returns The root of the modified tree with odd levels reversed
20 */
21function reverseOddLevels(root: TreeNode | null): TreeNode | null {
22 // Initialize queue with root node for level-order traversal
23 const currentLevelNodes: TreeNode[] = [root];
24
25 // Process each level of the tree
26 for (let levelIndex = 0; currentLevelNodes.length > 0; levelIndex++) {
27 // Check if current level is odd (1, 3, 5, ...)
28 if (levelIndex % 2 === 1) {
29 // Reverse values of nodes at odd levels using two-pointer approach
30 let leftPointer = 0;
31 let rightPointer = currentLevelNodes.length - 1;
32
33 while (leftPointer < rightPointer) {
34 // Swap values of nodes at left and right positions
35 const tempValue = currentLevelNodes[leftPointer].val;
36 currentLevelNodes[leftPointer].val = currentLevelNodes[rightPointer].val;
37 currentLevelNodes[rightPointer].val = tempValue;
38
39 leftPointer++;
40 rightPointer--;
41 }
42 }
43
44 // Prepare nodes for the next level
45 const nextLevelNodes: TreeNode[] = [];
46
47 // Add all children of current level nodes to the next level queue
48 for (const node of currentLevelNodes) {
49 // Only add children if they exist (check left child as indicator)
50 if (node.left !== null) {
51 nextLevelNodes.push(node.left);
52 nextLevelNodes.push(node.right);
53 }
54 }
55
56 // Replace current level nodes with next level nodes
57 currentLevelNodes.splice(0, currentLevelNodes.length, ...nextLevelNodes);
58 }
59
60 return root;
61}
62
Time and Space Complexity
Time Complexity: O(n)
where n
is the number of nodes in the binary tree.
The algorithm uses BFS (Breadth-First Search) to traverse the tree level by level. Each node is visited exactly once when it's dequeued from the queue. For odd levels, we perform value swaps which takes O(k)
time where k
is the number of nodes at that level. Since the total number of nodes across all levels is n
, and each node is processed once (either just traversed or traversed + swapped), the overall time complexity is O(n)
.
Space Complexity: O(n)
where n
is the number of nodes in the binary tree.
The space complexity is determined by the queue q
used for BFS traversal. In the worst case, the queue holds all nodes at the widest level of the tree. For a perfect binary tree (which this problem assumes based on the operation), the last level contains approximately n/2
nodes. Therefore, the maximum space used by the queue is O(n/2)
, which simplifies to O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Attempting to Reverse After Processing the Current Level
A critical mistake is trying to reverse node values after already removing nodes from the queue for processing. This leads to attempting operations on an empty or partially processed queue.
Incorrect Implementation:
while queue:
level_size = len(queue)
# Process nodes first (WRONG ORDER!)
for _ in range(level_size):
node = queue.popleft()
if node.left:
queue.append(node.left)
queue.append(node.right)
# Try to reverse after nodes are already removed
if level & 1:
# Queue no longer contains current level nodes!
left, right = 0, len(queue) - 1
while left < right:
queue[left].val, queue[right].val = queue[right].val, queue[left].val
left += 1
right -= 1
Why This Fails:
- After the for loop, the queue contains nodes from the NEXT level, not the current odd level
- You end up reversing the wrong level's values or getting index errors
Correct Approach:
- Always reverse BEFORE processing/removing nodes from the queue
- The queue should contain all nodes of the current level when reversing
2. Modifying Tree Structure Instead of Just Values
Another common mistake is trying to swap entire nodes or modify parent-child relationships instead of just swapping values.
Incorrect Approach:
# Trying to swap node references (WRONG!) queue[left], queue[right] = queue[right], queue[left]
Why This Fails:
- This only swaps references in the queue, not in the actual tree
- Parent-child relationships remain unchanged
- The tree structure gets corrupted or remains unmodified
Correct Approach:
- Only swap the values:
queue[left].val, queue[right].val = queue[right].val, queue[left].val
- Keep the tree structure intact
3. Using Wrong Level Indexing
Starting the level counter at 1 instead of 0, or incrementing at the wrong time.
Incorrect Implementation:
level = 1 # Starting at 1 (WRONG!) while queue: if level & 1: # This would reverse even levels (0-indexed) # reverse logic
Why This Fails:
- The root is at level 0 (even), its children at level 1 (odd)
- Starting at 1 would treat the root's children as even level
- Results in reversing the wrong levels
Correct Approach:
- Initialize
level = 0
- Increment level AFTER processing all nodes at the current level
- Remember: level 1, 3, 5... are odd (where we want to reverse)
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
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 dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
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
Want a Structured Path to Master System Design Too? Don’t Miss This!