A clear explanation of returning binary tree levels from bottom to top using breadth-first search.
Problem Restatement
We are given the root of a binary tree.
We need to return the node values level by level, but from bottom to top. Inside each level, values must still be ordered from left to right.
The official problem asks for the bottom-up level order traversal of the binary tree’s node values, from leaf level to root level.
For this tree:
3
/ \
9 20
/ \
15 7Normal level order traversal is:
[[3], [9, 20], [15, 7]]Bottom-up level order traversal is:
[[15, 7], [9, 20], [3]]Input and Output
| Item | Meaning |
|---|---|
| Input | root, the root node of a binary tree |
| Output | A list of lists of integers |
| Level order | Nodes are grouped by depth |
| Inside each level | Values stay left to right |
| Final order | Deepest level first, root level last |
| Empty tree | Return [] |
LeetCode gives the TreeNode class:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = rightThe function shape is:
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> list[list[int]]:
...Examples
Consider this tree:
3
/ \
9 20
/ \
15 7Level 0 contains:
[3]Level 1 contains:
[9, 20]Level 2 contains:
[15, 7]Normal top-down order is:
[[3], [9, 20], [15, 7]]The problem wants bottom-up order, so we reverse the list of levels:
[[15, 7], [9, 20], [3]]For a single-node tree:
1The answer is:
[[1]]For an empty tree:
root = NoneThe answer is:
[]First Thought: Use Normal Level Order Traversal
The problem still asks for nodes grouped by level.
That is exactly what breadth-first search gives us.
The only difference from LeetCode 102 is the final order of the levels.
So we can:
- Run normal BFS from top to bottom.
- Store each level as a list.
- Reverse the final answer.
This keeps the traversal simple and makes the bottom-up part explicit.
Key Insight
We should preserve left-to-right order inside each level.
That means during BFS, we still enqueue children in this order:
left child first
right child secondThis gives each level in the correct left-to-right order.
After all levels are collected, we reverse only the outer list:
ans.reverse()We do not reverse each inner level.
For example:
[[3], [9, 20], [15, 7]]becomes:
[[15, 7], [9, 20], [3]]The level [15, 7] remains left to right.
Algorithm
If root is None, return [].
Otherwise:
- Create a queue containing
root. - Create an empty answer list.
- While the queue is not empty:
- Create an empty list for the current level.
- Read the current queue size.
- Process exactly that many nodes.
- Append each node value to the current level list.
- Add the left child if it exists.
- Add the right child if it exists.
- Append the current level list to the answer.
- Reverse the answer list.
- Return the answer.
Correctness
The queue starts with the root, so the first processed level is the root level.
At the start of each outer loop, the queue contains exactly the nodes of one level in left-to-right order. We record the queue size before processing that level, so newly added children are not mixed into the current level.
For every processed node, the algorithm adds the left child before the right child. Therefore, the next level is also arranged from left to right in the queue.
Thus, before the final reversal, ans contains all levels from root to leaves, with left-to-right order inside each level.
The problem asks for the same levels from leaves to root. Reversing the outer list changes only the order of levels. It does not change the order of values inside each level.
Therefore, the returned list is exactly the required bottom-up level order traversal.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Each node is processed once |
| Space | O(n) | The queue and output can store node values |
Here, n is the number of nodes in the tree.
The final reversal costs O(L), where L is the number of levels. Since L <= n, the total time remains O(n).
Implementation
from collections import deque
from typing import Optional
# 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 levelOrderBottom(self, root: Optional[TreeNode]) -> list[list[int]]:
if root is None:
return []
ans = []
q = deque([root])
while q:
level = []
for _ in range(len(q)):
node = q.popleft()
level.append(node.val)
if node.left is not None:
q.append(node.left)
if node.right is not None:
q.append(node.right)
ans.append(level)
ans.reverse()
return ansCode Explanation
Handle the empty tree first:
if root is None:
return []Create the answer list and queue:
ans = []
q = deque([root])The queue lets us process nodes in breadth-first order.
The outer loop processes one level at a time:
while q:For each level, start a new list:
level = []This loop runs exactly once for every node in the current level:
for _ in range(len(q)):Remove the next node and store its value:
node = q.popleft()
level.append(node.val)Add children for the next level:
if node.left is not None:
q.append(node.left)
if node.right is not None:
q.append(node.right)After the current level is complete, store it:
ans.append(level)Finally, reverse the level order:
ans.reverse()
return ansTesting
from collections import deque
from typing import Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> list[list[int]]:
if root is None:
return []
ans = []
q = deque([root])
while q:
level = []
for _ in range(len(q)):
node = q.popleft()
level.append(node.val)
if node.left is not None:
q.append(node.left)
if node.right is not None:
q.append(node.right)
ans.append(level)
ans.reverse()
return ans
def run_tests():
s = Solution()
root1 = TreeNode(
3,
TreeNode(9),
TreeNode(20, TreeNode(15), TreeNode(7)),
)
assert s.levelOrderBottom(root1) == [[15, 7], [9, 20], [3]]
root2 = TreeNode(1)
assert s.levelOrderBottom(root2) == [[1]]
root3 = None
assert s.levelOrderBottom(root3) == []
root4 = TreeNode(
1,
TreeNode(2, TreeNode(4), TreeNode(5)),
TreeNode(3, TreeNode(6), TreeNode(7)),
)
assert s.levelOrderBottom(root4) == [[4, 5, 6, 7], [2, 3], [1]]
root5 = TreeNode(
1,
TreeNode(2, TreeNode(3, TreeNode(4))),
None,
)
assert s.levelOrderBottom(root5) == [[4], [3], [2], [1]]
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
[3,9,20,null,null,15,7] | Standard example |
| Single node | Minimum non-empty tree |
| Empty tree | Confirms empty output |
| Complete tree | Confirms each level stays left to right |
| Skewed tree | Confirms bottom-up order with one node per level |