Approach Summary
BFS with a queue. At each level, process all nodes in the queue (current level size), then enqueue their children.
Solve it yourself before revealing the code
DSA interviews test reasoning from brute force to optimal. Think through the constraints first.
Solution Progression (Brute Force → Most Optimized)
Understand the architectural evolution from naive baseline to optimal complexity.
| Approach | Time | Space | Core Strategy / Trade-off |
|---|---|---|---|
| 1 Approach 1: BFS with Queue Level Sizing (Optimal) | O(n) | O(w) | Snapshot queue length at each level, popping exactly that many nodes before advancing. |
| 2 Approach 2: Recursive DFS with Level Depth Indexing | O(n) | O(n) | Recursively visits nodes while tracking depth index, inserting values into the corresponding level list. |
| 3 Approach 2: Recursive DFS with Level Depth Indexing | O(n) | O(n) | Recursively visits nodes while tracking depth index, inserting values into the corresponding level list. |
Approach 1: BFS with Queue Level Sizing (Optimal)
Measure the queue size at the start of each level iteration. Pop exactly that number of nodes, append their values to a level array, and enqueue their children. When the inner loop finishes, push the level array to result.
Approach 1: BFS with Queue Level Sizing (Optimal) Implementation
Approach 1: BFS with Queue Level Sizing (Optimal)
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Queue<TreeNode> q = new LinkedList<>();
q.add(root);
while (!q.isEmpty()) {
int sz = q.size();
List<Integer> lvl = new ArrayList<>();
for (int i = 0; i < sz; i++) {
TreeNode n = q.poll();
lvl.add(n.val);
if (n.left != null) q.add(n.left);
if (n.right != null) q.add(n.right);
}
res.add(lvl);
}
return res;
}
}class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
int sz = q.size();
vector<int> lvl;
for (int i = 0; i < sz; i++) {
TreeNode* n = q.front(); q.pop();
lvl.push_back(n->val);
if (n->left) q.push(n->left);
if (n->right) q.push(n->right);
}
res.push_back(lvl);
}
return res;
}
};from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> list[list[int]]:
if not root:
return []
q, res = deque([root]), []
while q:
sz = len(q)
lvl = []
for _ in range(sz):
n = q.popleft()
lvl.append(n.val)
if n.left: q.append(n.left)
if n.right: q.append(n.right)
res.append(lvl)
return resfunction levelOrder(root) {
if (!root) return [];
const q = [root], res = [];
while (q.length > 0) {
const sz = q.length, lvl = [];
for (let i = 0; i < sz; i++) {
const n = q.shift();
lvl.push(n.val);
if (n.left) q.push(n.left);
if (n.right) q.push(n.right);
}
res.push(lvl);
}
return res;
} Approach 2: Recursive DFS with Level Depth Indexing
Passes an integer level along with each node. If level == result.size(), appends a new empty list. Appends node.val to result[level], then recurses left and right with level + 1.
Approach 2: Recursive DFS with Level Depth Indexing Implementation
Approach 2: Recursive DFS with Level Depth Indexing
class Solution {
private List<List<Integer>> res = new ArrayList<>();
private void dfs(TreeNode node, int level) {
if (node == null) return;
if (res.size() == level) res.add(new ArrayList<>());
res.get(level).add(node.val);
dfs(node.left, level + 1);
dfs(node.right, level + 1);
}
public List<List<Integer>> levelOrder(TreeNode root) {
dfs(root, 0);
return res;
}
}class Solution {
vector<vector<int>> res;
void dfs(TreeNode* node, int level) {
if (!node) return;
if (res.size() == level) res.push_back({});
res[level].push_back(node->val);
dfs(node->left, level + 1);
dfs(node->right, level + 1);
}
public:
vector<vector<int>> levelOrder(TreeNode* root) {
dfs(root, 0);
return res;
}
};class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> list[list[int]]:
res = []
def dfs(node, level):
if not node: return
if len(res) == level:
res.append([])
res[level].append(node.val)
dfs(node.left, level + 1)
dfs(node.right, level + 1)
dfs(root, 0)
return resfunction levelOrder(root) {
const res = [];
function dfs(node, level) {
if (!node) return;
if (res.length === level) res.push([]);
res[level].push(node.val);
dfs(node.left, level + 1);
dfs(node.right, level + 1);
}
dfs(root, 0);
return res;
} Approach 2: Recursive DFS with Level Depth Indexing
Passes an integer level along with each node. If level == result.size(), appends a new empty list. Appends node.val to result[level], then recurses left and right with level + 1.
Approach 2: Recursive DFS with Level Depth Indexing Implementation
Approach 2: Recursive DFS with Level Depth Indexing
class Solution {
private List<List<Integer>> res = new ArrayList<>();
private void dfs(TreeNode node, int level) {
if (node == null) return;
if (res.size() == level) res.add(new ArrayList<>());
res.get(level).add(node.val);
dfs(node.left, level + 1);
dfs(node.right, level + 1);
}
public List<List<Integer>> levelOrder(TreeNode root) {
dfs(root, 0);
return res;
}
}class Solution {
vector<vector<int>> res;
void dfs(TreeNode* node, int level) {
if (!node) return;
if (res.size() == level) res.push_back({});
res[level].push_back(node->val);
dfs(node->left, level + 1);
dfs(node->right, level + 1);
}
public:
vector<vector<int>> levelOrder(TreeNode* root) {
dfs(root, 0);
return res;
}
};class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> list[list[int]]:
res = []
def dfs(node, level):
if not node: return
if len(res) == level:
res.append([])
res[level].append(node.val)
dfs(node.left, level + 1)
dfs(node.right, level + 1)
dfs(root, 0)
return resfunction levelOrder(root) {
const res = [];
function dfs(node, level) {
if (!node) return;
if (res.length === level) res.push([]);
res[level].push(node.val);
dfs(node.left, level + 1);
dfs(node.right, level + 1);
}
dfs(root, 0);
return res;
} Edge Cases to Watch
- Empty tree (returns empty list [])
- Single node (returns [[root.val]])
- Deeply unbalanced tree (e.g. linked list; each level has 1 node)
Master the Queue / BFS Pattern
Study the core invariant, step-by-step decision framework, and common interview traps for the Queue / BFS pattern.
How to Recognize This Pattern
- Level-by-level tree output
Complexity Analysis
Time Complexity
O(n)
Space Complexity
O(n)