Given the root
of a binary tree, calculate the vertical order traversal of the binary tree. For each node at position (row, col)
, its left and right children will be at positions (row + 1, col - 1)
and (row + 1, col + 1)
respectively. The root of the tree is at (0, 0)
. The vertical order traversal of a binary tree is a list of top-to-bottom orderings for each column index starting from the leftmost column and ending on the rightmost column. There may be multiple nodes in the same row and same column. In such a case, sort these nodes by their values. Return the vertical order traversal of the binary tree. For example, given the tree [3,9,20,null,null,15,7]
, return [[9],[3,15],[20],[7]]
. As another example, given the tree [1,2,3,4,5,6,7]
, return [[4],[2],[1,5,6],[3],[7]]
. The number of nodes in the tree is in the range [1, 1000]
and 0 <= Node.val <= 1000
.
## Vertical Order Traversal of a Binary Tree
This problem asks us to perform a vertical order traversal of a binary tree. For each node, we assign coordinates (row, col) where the root is at (0, 0). Left children are at (row + 1, col - 1), and right children are at (row + 1, col + 1). The goal is to return a list of top-to-bottom orderings for each column index, from leftmost to rightmost. When nodes share the same row and column, they are sorted by their values.
### 1. Naive Solution (Using Hashmap and Sorting)
A naive approach involves traversing the tree and storing nodes along with their column indices in a hashmap. The keys of the hashmap represent the column indices, and the values are lists of nodes in that column. After traversing the tree, we sort the column indices and then sort the nodes within each column based on their row and value.
```python
from collections import defaultdict, deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def vertical_order_traversal_naive(root):
if not root:
return []
column_map = defaultdict(list)
queue = deque([(root, 0, 0)]) # (node, row, col)
while queue:
node, row, col = queue.popleft()
column_map[col].append((row, node.val))
if node.left:
queue.append((node.left, row + 1, col - 1))
if node.right:
queue.append((node.right, row + 1, col + 1))
sorted_columns = sorted(column_map.keys())
result = []
for col in sorted_columns:
column_nodes = sorted(column_map[col], key=lambda x: (x[0], x[1])) # Sort by row, then value
result.append([node[1] for node in column_nodes])
return result
We can optimize the sorting step by using a priority queue (min-heap) to store nodes within each column. The priority queue will automatically maintain the nodes in sorted order based on their row and value. This eliminates the need for explicit sorting after the traversal.
from collections import defaultdict, deque
import heapq
def vertical_order_traversal_optimal(root):
if not root:
return []
column_map = defaultdict(list)
queue = deque([(root, 0, 0)]) # (node, row, col)
while queue:
node, row, col = queue.popleft()
heapq.heappush(column_map[col], (row, node.val))
if node.left:
queue.append((node.left, row + 1, col - 1))
if node.right:
queue.append((node.right, row + 1, col + 1))
sorted_columns = sorted(column_map.keys())
result = []
for col in sorted_columns:
column_nodes = []
while column_map[col]:
column_nodes.append(heapq.heappop(column_map[col])[1]) # Extract value from the heap
result.append(column_nodes)
return result
defaultdict
automatically handles the creation of new columns as needed.defaultdict
, which allows for negative keys.By using a defaultdict
and a priority queue (in the optimal solution), we can efficiently perform the vertical order traversal while handling edge cases and duplicate values appropriately. The priority queue ensures that nodes within each column are correctly ordered without the need for explicit sorting after the traversal.