Compute the binary tree's vertical order traversal given its root.
The left and right children of a node at location (row, col) will be at (row + 1, col - 1) and (row + 1, col + 1), respectively. The tree's root is located 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 binary tree's vertical order traversal.
Input : root = [3, 9, 20, null, null, 15, 7]
Output : [ [9] , [3, 15] , [20] , [7] ]
Explanation :
Column -1: Only node 9 is in this column.
Column 0: Nodes 3 and 15 are in this column in that order from top to bottom.
Column 1: Only node 20 is in this column.
Column 2: Only node 7 is in this column.
Input : root = [1, 2, 3, 4, 5, 6, 7]
Output : [ [4] , [2] , [1, 5, 6] , [3] , [7] ]
Explanation :
Column -2: Only node 4 is in this column.
Column -1: Only node 2 is in this column.
Column 0: Nodes 1, 5, and 6 are in this column.1 is at the top, so it comes first. 5 and 6 are at the same position (2, 0), so we order them by their value, 5 before 6.
Column 1: Only node 3 is in this column.
Column 2: Only node 7 is in this column.
Input : root = [5, 1, 2, 8, null, 4, 5, null, 6]
The vertical order traversal of a binary tree involves organizing nodes based on their horizontal and vertical positions relative to their parent nodes. Each node can be categorized by its vertical column ('x') and level ('y'). Nodes with the same 'x' value are aligned vertically, forming columns, while 'y' represents the depth or level within the tree.
To achieve this traversal, use a level order BFS approach, starting from the root node. This ensures that nodes are processed level by level, and within each level, nodes are processed from left to right. By maintaining a map structure that uses 'x' as keys for vertical columns and 'y' as keys within a nested map for levels, we store node values in a multiset to maintain uniqueness and sorting.
#include<bits/stdc++.h>
using namespace std;
struct TreeNode {
int data;
TreeNode* left;
TreeNode* right;
TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};
class Solution {
public:
vector<vector<int>> verticalTraversal(TreeNode* root) {
// List to store the final result
vector<vector<int>> result;
if (root == nullptr) {
return result;
}
// Map to store the nodes at each vertical distance and level
map<int, map<int, priority_queue<int, vector<int>, greater<int>>>> nodesMap;
// Queue for BFS traversal (stores node along with its x and y coordinates)
queue<pair<TreeNode*, pair<int, int>>> q;
q.push({root, {0, 0}}); // (node, {x, y})
// Perform BFS
while (!q.empty()) {
auto p = q.front();
q.pop();
TreeNode* node = p.first;
int x = p.second.first;
int y = p.second.second;
// Add the node's value to the map at the correct x and y
nodesMap[x][y].push(node->data);
// Add the left child with updated coordinates to the queue
if (node->left != nullptr) {
q.push({node->left, {x - 1, y + 1}});
}
// Add the right child with updated coordinates to the queue
if (node->right != nullptr) {
q.push({node->right, {x + 1, y + 1}});
}
}
// Prepare the result by sorting keys and compiling nodes
for (auto& p : nodesMap) {
vector<int> column;
for (auto& q : p.second) {
while (!q.second.empty()) {
column.push_back(q.second.top());
q.second.pop();
}
}
result.push_back(column);
}
return result;
}
};
// Main method to test the verticalTraversal function
int main() {
// Creating a binary tree
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
// Creating an instance of Solution
Solution sol;
// Getting the vertical order traversal
vector<vector<int>> result = sol.verticalTraversal(root);
// Printing the result
cout << "Vertical Order Traversal: " << endl;
for (const auto& col : result) {
for (int num : col) {
cout << num << " ";
}
cout << endl;
}
return 0;
}
import java.util.*;
class TreeNode {
int data;
TreeNode left;
TreeNode right;
TreeNode(int val) {
data = val;
left = null;
right = null;
}
}
class Solution {
// Nested static class Tuple
static class Tuple {
TreeNode node;
int x; // Vertical distance
int y; // Level
Tuple(TreeNode node, int x, int y) {
this.node = node;
this.x = x;
this.y = y;
}
}
public List<List<Integer>> verticalTraversal(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) {
return result;
}
// TreeMap to store the nodes at each vertical distance
TreeMap<Integer, TreeMap<Integer, PriorityQueue<Integer>>> nodesMap = new TreeMap<>();
// Queue for BFS traversal (stores node along with its x and y coordinates)
Queue<Tuple> queue = new LinkedList<>();
queue.offer(new Tuple(root, 0, 0)); // (node, x, y)
// Perform BFS
while (!queue.isEmpty()) {
Tuple tuple = queue.poll();
TreeNode node = tuple.node;
int x = tuple.x;
int y = tuple.y;
// Add the node's value to the map at the correct x and y
nodesMap.putIfAbsent(x, new TreeMap<>());
nodesMap.get(x).putIfAbsent(y, new PriorityQueue<>());
nodesMap.get(x).get(y).offer(node.data);
// Add the left child with updated coordinates to the queue
if (node.left != null) {
queue.offer(new Tuple(node.left, x - 1, y + 1));
}
// Add the right child with updated coordinates to the queue
if (node.right != null) {
queue.offer(new Tuple(node.right, x + 1, y + 1));
}
}
// Prepare the result by sorting keys and compiling nodes
for (TreeMap<Integer, PriorityQueue<Integer>> yMap : nodesMap.values()) {
List<Integer> column = new ArrayList<>();
for (PriorityQueue<Integer> nodes : yMap.values()) {
while (!nodes.isEmpty()) {
column.add(nodes.poll());
}
}
result.add(column);
}
return result;
}
}
class Main {
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
root.right.right = new TreeNode(7);
Solution solution = new Solution();
List<List<Integer>> result = solution.verticalTraversal(root);
System.out.println("Vertical Traversal:");
for (List column : result) {
System.out.println(column);
}
}
}
from collections import defaultdict, deque
# Definition for a binary tree node.
class TreeNode(object):
def __init__(self, val=0, left=None, right=None):
self.data = val
self.left = left
self.right = right
class Solution:
def verticalTraversal(self, root):
"""
Perform vertical order traversal of a binary tree.
:param root: TreeNode - The root of the binary tree.
:return: List[List[int]] - A list of lists containing the vertical order traversal.
"""
if not root:
return []
# Dictionary to store the nodes at each vertical distance and level.
# nodes_map[x][y] will hold a list of nodes at vertical distance x and level y.
nodes_map = defaultdict(lambda: defaultdict(list))
# Queue for BFS traversal (stores node along with its x and y coordinates)
queue = deque([(root, 0, 0)]) # (node, x, y)
# Perform BFS to populate nodes_map with nodes at each (x, y) coordinate.
while queue:
node, x, y = queue.popleft()
# Add the node's value to the map at the correct x and y
nodes_map[x][y].append(node.data)
# Add the left child with updated coordinates to the queue
if node.left:
queue.append((node.left, x - 1, y + 1))
# Add the right child with updated coordinates to the queue
if node.right:
queue.append((node.right, x + 1, y + 1))
# Prepare the result by sorting keys and compiling nodes
result = []
for x in sorted(nodes_map):
column = []
for y in sorted(nodes_map[x]):
# Sort the nodes at the same position to maintain the order
column.extend(sorted(nodes_map[x][y]))
result.append(column)
return result
# Main method to test the verticalTraversal function
if __name__ == "__main__":
# Creating a binary tree
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
root.right.left = TreeNode(6)
root.right.right = TreeNode(7)
# Creating an instance of Solution
sol = Solution()
# Getting the vertical order traversal
result = sol.verticalTraversal(root)
# Printing the result
print("Vertical Order Traversal:", result)
// Definition for a binary tree node.
class TreeNode {
constructor(val = 0, left = null, right = null){
this.data = val;
this.left = left;
this.right = right;
}
}
class Solution {
verticalTraversal(root) {
// If the root is null, return an empty array
if (!root) {
return [];
}
// Map to store the nodes at each vertical distance and level
const nodesMap = new Map();
// Queue for BFS traversal (stores node along with its x and y coordinates)
const queue = [{ node: root, x: 0, y: 0 }];
// Perform BFS
while (queue.length > 0) {
const { node, x, y } = queue.shift();
// Add the node's value to the map at the correct x and y
if (!nodesMap.has(x)) {
nodesMap.set(x, new Map());
}
if (!nodesMap.get(x).has(y)) {
nodesMap.get(x).set(y, []);
}
nodesMap.get(x).get(y).push(node.data);
// Add the left child with updated coordinates to the queue
if (node.left) {
queue.push({ node: node.left, x: x - 1, y: y + 1 });
}
// Add the right child with updated coordinates to the queue
if (node.right) {
queue.push({ node: node.right, x: x + 1, y: y + 1 });
}
}
// Prepare the result by sorting keys and compiling nodes
const result = [];
const sortedXKeys = Array.from(nodesMap.keys()).sort((a, b) => a - b);
for (const x of sortedXKeys) {
const yMap = nodesMap.get(x);
const sortedYKeys = Array.from(yMap.keys()).sort((a, b) => a - b);
const column = [];
for (const y of sortedYKeys) {
const nodes = yMap.get(y);
nodes.sort((a, b) => a - b);
column.push(...nodes);
}
result.push(column);
}
return result;
}
}
// Main method to test the verticalTraversal function
// Creating a binary tree
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.left = new TreeNode(6);
root.right.right = new TreeNode(7);
// Creating an instance of Solution
const sol = new Solution();
// Getting the vertical order traversal
const result = sol.verticalTraversal(root);
// Printing the result
console.log("Vertical Order Traversal:", result);
Time Complexity:O(N * log2N * log2N * log2N) : This complexity arises from performing postorder traversal using BFS, where each node's insertion and retrieval operations in nested maps take logarithmic time. Overall, it reflects the combined cost of processing each node and managing the node mappings.
Space Complexity: O(N + N/2) : The space usage is dominated by the map storing nodes by their vertical and level information, occupying O(N) space. Additionally, the queue for BFS can occupy up to O(N/2) space in a balanced tree's worst-case scenario, contributing to the total space complexity.
Q: Why do we sort by both row index and node value?
A: Sorting ensures that when multiple nodes are in the same column, they appear top-to-bottom, and in case of a tie, they appear in ascending order of value.
Q: Why does BFS work better than DFS in this case?
A: BFS naturally processes nodes level by level, making it easier to track row index without recursion overhead. DFS can be used, but it requires explicit tracking of row and column indices during recursion.
Q: What if diagonal traversal was required instead of vertical order?
A: In diagonal traversal, nodes at (row, col) map to diagonal = row - col. Instead of grouping by column, we would group by diagonal index.
Q: How does this compare to level order traversal?
A: Level order traversal processes nodes row-wise, whereas vertical order traversal processes nodes column-wise.