Leetcode 655. Print Binary Tree

Problem Explanation

The problem is asking to print the binary tree in m*n 2D string array. Here, m represents the height of the tree and n is always an odd number. The root of the tree should be in the middle of the first row. For the rest of the spaces in the array that do not include the node value, an empty string should be added. If a node has a left or right child, those sub-trees should be considered as separate trees and the same procedure should be applied recursively.

Let's take an example to understand clearly:

Consider a tree:

1
2
3  1
4 / \
52   3
6 \
7  4

In this case, the root of the tree is 1. So it should be in the middle of the first row. The second row will be divided into left and right sub-trees. The left sub-tree [2] consists of the root node 2 and one right child 4. According to the rule, the node 2 should sit at the midpoint in the left part of the same row that contains its parent node 1. So, it will be right above to the left of node 1. Applying the same procedure to the right sub-tree [4] and the remaining sub-trees, we would get a result like this.

1
2
3[["", "", "", "1", "", "", ""],
4 ["", "2", "", "", "", "3", ""],
5 ["", "", "4", "", "", "", ""]]

Solution Approach

The solution is using Depth-First Search (DFS) algorithm to go through each node in the tree. Here, first we will calculate the height of the tree which tells us how many rows would be there in the 2D array. Then we find the width of the array using the height which would always be an odd number. Calculate the midpoint to place the root and then divide the array recursively for the left and right sub-trees.

Let's break it down into steps:

  1. Get the height of the binary tree (maxHeight).
  2. Initialize 2D array using height(maxHeight) and width (2^maxHeight -1).
  3. Now start DFS, for each node, put it in the middle of its range and then dfs its children.
  4. For each node, if the current level is 'd', start position is 'i', end position is 'j', we can put it at position (i+j)/2 and next level the start position for left child is 'i', right child 'j' and end position for left child is (i+j)/2, right child is (i+j)/2.

Python Solution

1
2python
3class TreeNode:
4  def __init__(self, x):
5    self.val = x
6    self.left = None
7    self.right = None
8
9class Solution:
10  def printTree(self, root: TreeNode):
11    def depth(root):
12      if not root: return 0
13      return max(depth(root.left), depth(root.right)) + 1
14
15    def fill(node, i, l, r):
16      if not node: return
17      m = (l + r) // 2
18      ans[i][m] = str(node.val)
19      fill(node.left, i + 1, l, m - 1)
20      fill(node.right, i + 1, m + 1, r)
21    
22    d = depth(root)
23    w = 2**d - 1
24    ans = [[""] * w for _ in range(d)]
25    fill(root, 0, 0, w - 1)
26    return ans

Java Solution

1
2java
3import java.util.ArrayList;
4import java.util.List;
5
6public class TreeNode {
7  int val;
8  TreeNode left;
9  TreeNode right;
10  TreeNode(int x) { val = x; }
11}
12
13
14public class Solution {
15  public List<List<String>> printTree(TreeNode root) {
16    int height = height(root);
17    int width = (1 << height) - 1;
18    List<String> row = new ArrayList<>();
19    for(int i = 0; i < width; i++) row.add("");
20    List<List<String>> ans = new ArrayList<>();
21    for(int i = 0; i < height; i++) ans.add(new ArrayList<>(row));
22    populate(root, ans, 0, height, 0, width - 1);
23    return ans;
24  }
25  
26  private void populate(TreeNode root, List<List<String>> ans, int i, int j, int l, int r) {
27    if (root == null || l > r) return;
28    ans.get(i).set(l + (r - l) / 2, Integer.toString(root.val));
29    populate(root.left, ans, i + 1, j, l, l + (r - l) / 2 - 1);
30    populate(root.right, ans, i + 1, j, l + (r - l) / 2 + 1, r);
31  }
32  
33  private int height(TreeNode root) {
34    if (root == null) return 0;
35    return 1 + Math.max(height(root.left), height(root.right));
36  }
37}

JavaScript Solution

1
2javascript
3class TreeNode {
4  constructor(val, left = null, right = null){
5    this.val = val;
6    this.left = left;
7    this.right = right;
8  }
9}
10class Solution {
11  printTree(root) {
12    const h = this.getHeight(root);
13    const w = Math.pow(2,h) -1;
14    const ans = Array.from({length: h}).map(() => Array.from({length: w}).fill(""));
15    this.fill(root, ans, 0, h, 0, w - 1);
16    return ans;
17  }
18  
19  getHeight(node){
20    if(!node) return 0;
21    let LH = this.getHeight(node.left);
22    let RH = this.getHeight(node.right);
23    return Math.max(LH, RH) + 1;
24  }
25
26  fill(node, ans, i, j, l, r){
27    if(!node || l > r) return;
28    ans[i][(l + r) >> 1] = `${node.val}`;
29    this.fill(node.left, ans, i + 1, j, l, l + ((r - l) >> 1) - 1);
30    this.fill(node.right, ans, i + 1, j, l + ((r - l) >> 1) + 1, r);
31  }
32};

C++ Solution

1
2cpp
3#include <vector>
4#include <string>
5#include <cmath>
6using namespace std;
7
8// Definition for a binary tree node.
9struct TreeNode {
10  int val;
11  TreeNode *left;
12  TreeNode *right;
13  TreeNode(int x) : val(x), left(NULL), right(NULL) {}
14};
15
16
17class Solution {
18public:
19    vector<vector<string>> printTree(TreeNode* root) {
20        // get height of the tree
21        int height = maxHeight(root);
22        // initialize 2D array
23        int width = (1 << height) - 1;
24        vector<vector<string>> ans(height, vector<string>(width, ""));
25        dfs(root, ans, 0, 0, width-1);
26        return ans;
27    }
28  
29private:
30    int maxHeight(TreeNode* root) {
31      if (root == NULL)
32      return 0;
33    return 1 + max(maxHeight(root->left), maxHeight(root->right));
34    }
35    
36    void dfs(TreeNode* root, vector<vector<string>>& ans, int level, int left, int right){
37        if(!root) return;
38        int mid = (left + right)/2;
39        ans[level][mid] = to_string(root->val);
40        dfs(root->left, ans, level + 1, left, mid - 1);
41        dfs(root->right, ans, level + 1, mid + 1, right);
42    }
43};

C# Solution

1
2csharp
3using System;
4using System.Collections.Generic;
5using System.Linq;
6
7public class TreeNode {
8  public int val;
9  public TreeNode left;
10  public TreeNode right;
11  public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
12    this.val = val;
13    this.left = left;
14    this.right = right;
15  }
16}
17public class Solution {
18  public IList<IList<string>> PrintTree(TreeNode root) {
19    int depth = GetDepth(root);
20    var result = new List<IList<string>>();
21    for(var i = 0; i < depth; ++i) {    
22      result.Add(Enumerable.Repeat("", (int)Math.Pow(2, depth) - 1).ToList());
23    }
24    Helper(root, 0, result[0].Count, 0, result);
25    return result;
26  }
27  private void Helper(TreeNode node, int start, int end, int depth, List<IList<string>> result) {
28    if(node == null) {
29      return;
30    }
31    int mid = (start + end) / 2;
32    result[depth][mid] = node.val.ToString();
33    Helper(node.left, start, mid - 1, depth + 1, result);
34    Helper(node.right, mid + 1, end, depth + 1, result);
35  }
36  private int GetDepth(TreeNode root) {
37    if(root == null) {
38      return 0;
39    }
40    return Math.Max(GetDepth(root.left), GetDepth(root.right)) + 1;
41  }
42}

Here, maxHeight function calculates the height of the tree using recursion. In printTree function, we calculate the width of the array and then initialize m*n 2D matrix ans. dfs function is used to navigate through each node in the tree and place them in the 2D array accordingly. If there's no node, it inserts an empty string.# Conclusion

Printing a binary tree in a 2D array format can be daunting, as we have to deal with placing each node correctly in a 2D space. However, we can solve this problem by decomposing it into some smaller problems. Calculating the height of the tree is the first important step to initialize the 2D array, and DFS algorithm is used to explore all the nodes and place them correctly. The key insight here is that each node's position is decided by the midpoint, which is computed by averaging the left and right indices of the current interval.

These solutions in Python, JavaScript, Java, C++, and C# all use a similar approach. They first define an initial two-dimensional string array to hold all nodes' values and then process each node level by level to put its value into the correct position in the array. We can test the validity of these solutions with various test cases to make sure they work as expected.

It's important to master such common techniques, as they can be applied in many other problems in tree-like data structures. Each node, its value, and relation with other nodes in such problems. As evident from the solution, the key to such problems is correctly understanding the problem statement and coming up with an algorithm that can efficiently solve the problem. The implementation part is often straight-forward once the strategy is clear.

Overall, printing a binary tree in a 2D array format is a creative problem that will challenge your understanding of tree and array data structures. Hope you enjoy solving it!


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫