Level Order Traversal of Binary Tree
Return node values grouped per depth using a queue-based BFS.
3 min read
tree bfs queue
Problem#
Given the root of a binary tree, return its level-order traversal: a list of lists where each inner list contains the values at one depth, left to right.
Examples#
[3,9,20,null,null,15,7]→[[3],[9,20],[15,7]][1]→[[1]][]→[]
Constraints#
0 <= n <= 2000,-1000 <= Node.val <= 1000.
Hints#
Hint 1
A queue gives breadth-first order naturally — but you need to know where each level ends.
Hint 2
Snapshot the queue size at the start of each level; that many pops belong to this depth.
Approach#
Push the root, then repeatedly drain exactly queue.size() nodes before moving to the next level — that snapshot delimits the boundary without sentinels.
Solution#
class Solution {public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> ans; if (!root) return ans; queue<TreeNode*> q; q.push(root); while (!q.empty()) { int sz = q.size(); vector<int> level; level.reserve(sz); for (int i = 0; i < sz; ++i) { TreeNode* cur = q.front(); q.pop(); level.push_back(cur->val); if (cur->left) q.push(cur->left); if (cur->right) q.push(cur->right); } ans.push_back(std::move(level)); } return ans; }};func levelOrder(root *TreeNode) [][]int { ans := [][]int{} if root == nil { return ans } queue := []*TreeNode{root} for len(queue) > 0 { sz := len(queue) level := make([]int, 0, sz) for i := 0; i < sz; i++ { cur := queue[0] queue = queue[1:] level = append(level, cur.Val) if cur.Left != nil { queue = append(queue, cur.Left) } if cur.Right != nil { queue = append(queue, cur.Right) } } ans = append(ans, level) } return ans}from collections import dequefrom typing import List, Optional
class Solution: def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: ans: List[List[int]] = [] if not root: return ans q = deque([root]) while q: level = [] for _ in range(len(q)): cur = q.popleft() level.append(cur.val) if cur.left: q.append(cur.left) if cur.right: q.append(cur.right) ans.append(level) return ansvar levelOrder = function(root) { const ans = []; if (!root) return ans; const q = [root]; while (q.length > 0) { const sz = q.length; const level = []; for (let i = 0; i < sz; i++) { const cur = q.shift(); level.push(cur.val); if (cur.left) q.push(cur.left); if (cur.right) q.push(cur.right); } ans.push(level); } return ans;};class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> ans = new ArrayList<>(); if (root == null) return ans; Deque<TreeNode> q = new ArrayDeque<>(); q.offer(root); while (!q.isEmpty()) { int sz = q.size(); List<Integer> level = new ArrayList<>(sz); for (int i = 0; i < sz; i++) { TreeNode cur = q.poll(); level.add(cur.val); if (cur.left != null) q.offer(cur.left); if (cur.right != null) q.offer(cur.right); } ans.add(level); } return ans; }}function levelOrder(root: TreeNode | null): number[][] { const ans: number[][] = []; if (!root) return ans; const q: TreeNode[] = [root]; while (q.length > 0) { const sz = q.length; const level: number[] = []; for (let i = 0; i < sz; i++) { const cur = q.shift()!; level.push(cur.val); if (cur.left) q.push(cur.left); if (cur.right) q.push(cur.right); } ans.push(level); } return ans;}Editorial#
Capturing the queue size before the inner loop is the trick: any node enqueued during the iteration belongs to the next level and is excluded by the fixed bound. This avoids depth tags or null sentinels.
Complexity#
- Time: O(n) — every node enqueued/dequeued once.
- Space: O(n) for the queue and output.
Concept revision#
Related#