You are given the root node of a binary tree, return the vertical order traversal of its nodes' values.
For the vertical order traversal, list the nodes column by column starting from the leftmost column and moving to the right.
Within each column, the nodes should be listed in the order they appear from the top of the tree to the bottom.
If two nodes are located at the same row and column, the node that appears to the left should come before the other.
Example 1:
Input: root = [3,9,20,null,null,15,7]
Output: [[9],[3,15],[20],[7]]Example 2:
Input: root = [3,9,8,4,0,1,7]
Output: [[4],[9],[3,0,1],[8],[7]]Example 3:
Input: root = [1,2,3,4,10,9,11,null,5,null,null,null,null,null,null,null,6]
Output: [[4],[2,5],[1,10,9,6],[3],[11]]Constraints:
0 <= number of nodes in the tree <= 100-100 <= Node.val <= 100Before attempting this problem, you should be comfortable with:
We assign each node a column index where the root is at column 0, left children are at column - 1, and right children are at column + 1. Using BFS ensures we visit nodes level by level, so nodes in the same column appear in top-to-bottom order. We group nodes by their column index and sort the columns to get the final result.
0).column - 1 and right child with column + 1.# 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 verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
cols = defaultdict(list)
que = deque([(root, 0)])
while que:
node, pos = que.popleft()
if node:
cols[pos].append(node.val)
que.append((node.left, pos - 1))
que.append((node.right, pos + 1))
return [cols[x] for x in sorted(cols)]DFS can also solve this problem, but we need to track each node's row to maintain the correct vertical order within columns. Since DFS doesn't naturally visit nodes in level order, we store both the row and value for each node. After traversal, we sort each column by row index to ensure nodes appear in top-to-bottom order.
(row, value) for each column.dfs function that takes the current node, row, and column.node is null, return.(row, node value) to the column's list.dfs on the left child with (row + 1, column - 1) and right child with (row + 1, column + 1).# 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 verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
cols = defaultdict(list)
def dfs(node, row, col):
if not node:
return
cols[col].append((row, node.val))
dfs(node.left, row + 1, col - 1)
dfs(node.right, row + 1, col + 1)
dfs(root, 0, 0)
res = []
for col in sorted(cols):
col_vals = sorted(cols[col], key=lambda x: x[0])
res.append([val for _, val in col_vals])
return resInstead of sorting all column keys at the end, we can track the minimum and maximum column indices during traversal. This allows us to iterate from the leftmost to rightmost column directly without sorting, reducing the time complexity.
0.minCol/maxCol.minCol to maxCol and collect each column's values in order.# 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 verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
cols = defaultdict(list)
queue = deque([(root, 0)])
minCol = maxCol = 0
while queue:
node, col = queue.popleft()
cols[col].append(node.val)
minCol = min(minCol, col)
maxCol = max(maxCol, col)
if node.left:
queue.append((node.left, col - 1))
if node.right:
queue.append((node.right, col + 1))
return [cols[c] for c in range(minCol, maxCol + 1)]Similar to the optimal BFS approach, we track min and max column indices during DFS to avoid sorting columns. However, we still need to store row information and sort within each column since DFS doesn't visit nodes in level order. This approach reduces the complexity of iterating over columns while still requiring per-column sorting.
(row, value) pairs for each column.minCol and maxCol column indices during traversal.dfs function that updates minCol/maxCol and adds (row, value) to the column list.dfs completes, iterate from minCol to maxCol.# 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 verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
cols = defaultdict(list)
minCol = maxCol = 0
def dfs(node, row, col):
nonlocal minCol, maxCol
if not node:
return
cols[col].append((row, node.val))
minCol = min(minCol, col)
maxCol = max(maxCol, col)
dfs(node.left, row + 1, col - 1)
dfs(node.right, row + 1, col + 1)
dfs(root, 0, 0)
res = []
for c in range(minCol, maxCol + 1):
# sort by row only
col_vals = sorted(cols[c], key=lambda x: x[0])
res.append([val for _, val in col_vals])
return resWhere is the number of nodes, is the height of the tree (i.e. maximum number of nodes in any vertical line of the tree), and is the width of the tree (i.e. maximum number of nodes in any of the levels of the tree).
DFS does not visit nodes level by level, so nodes in the same column may be visited out of order. Without storing the row index alongside each value, nodes at the same column will appear in traversal order rather than top-to-bottom order, violating the problem's requirement.
When two nodes share the same row and column (which can happen with certain tree structures), the node that appears first from left to right in the tree should come first in the result. BFS naturally handles this, but DFS with unstable sorting can break this ordering.
# Wrong: using regular sort instead of stable sort
cols[col].sort(key=lambda x: x[0]) # May reorder same-row nodesSorting all column keys at the end works but is less efficient. A common mistake is not realizing that column indices are consecutive integers from minCol to maxCol, allowing direct iteration without sorting.