Construct Quad Tree
MedPrompt
Given a n * n matrix grid of 0's and 1's only. We want to represent grid with a Quad-Tree.
Return the root of the Quad-Tree representing grid.
A Quad-Tree is a tree data structure in which each internal node has exactly four children. Besides, each node has two attributes:
val: True if the node represents a grid of 1's or False if the node represents a grid of 0's. Notice that you can assign thevalto True or False whenisLeafis False, and both are accepted in the answer.isLeaf: True if the node is a leaf node on the tree or False if the node has four children.
class Node {
public boolean val;
public boolean isLeaf;
public Node topLeft;
public Node topRight;
public Node bottomLeft;
public Node bottomRight;
}We can construct a Quad-Tree from a two-dimensional area using the following steps:
- If the current grid has the same value (i.e all
1'sor all0's) setisLeafTrue and setvalto the value of the grid and set the four children to Null and stop. - If the current grid has different values, set
isLeafto False and setvalto any value and divide the current grid into four sub-grids as shown in the photo. - Recurse for each of the children with the proper sub-grid.
If you want to know more about the Quad-Tree, you can refer to the wiki.
Quad-Tree format:
You don't need to read this section for solving the problem. This is only if you want to understand the output format here. The output represents the serialized format of a Quad-Tree using level order traversal, where null signifies a path terminator where no node exists below.
It is very similar to the serialization of the binary tree. The only difference is that the node is represented as a list [isLeaf, val].
If the value of isLeaf or val is True we represent it as 1 in the list [isLeaf, val] and if the value of isLeaf or val is False we represent it as 0.
Example 1:
Input: grid = [[0,1],[1,0]]
Output: [[0,1],[1,0],[1,1],[1,1],[1,0]]
Explanation: The explanation of this example is shown below:
Notice that 0 represents False and 1 represents True in the photo representing the Quad-Tree.Example 2:

Input: grid = [[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0]]
Output: [[0,1],[1,1],[0,1],[1,1],[1,0],null,null,null,null,[1,0],[1,0],[1,1],[1,1]]
Explanation: All values in the grid are not the same. We divide the grid into four sub-grids.
The topLeft, bottomLeft and bottomRight each has the same value.
The topRight have different values so we divide it into 4 sub-grids where each has the same value.
Explanation is shown in the photo below:
Constraints:
n == grid.length == grid[i].lengthn == 2xwhere0 <= x <= 6
Approaches
3 approaches with complexity analysis and trade-offs.
This approach directly implements the recursive definition of the Quad Tree construction. A helper function is defined that takes the coordinates and size of the current sub-grid. This function first checks if all values in the sub-grid are the same. If they are, it creates a leaf node. If not, it creates an internal node, divides the grid into four equal quadrants, and makes a recursive call for each quadrant to build the sub-trees.
Algorithm
- Create a recursive helper function
build(grid, row, col, size). - In the helper function, check if the sub-grid from
(row, col)ofsize x sizeis homogeneous. - To check for homogeneity, iterate through all
size * sizecells and compare them to the first cell's value. - If the sub-grid is homogeneous, create and return a leaf node with
isLeaf=trueandvalset to the common value. - If the sub-grid is not homogeneous, create an internal node with
isLeaf=false. - Recursively call
buildfor the four quadrants:topLeft = build(grid, row, col, size/2)topRight = build(grid, row, col + size/2, size/2)bottomLeft = build(grid, row + size/2, col, size/2)bottomRight = build(grid, row + size/2, col + size/2, size/2)
- Assign the results to the children of the internal node and return it.
- The initial call is
construct(grid)which callsbuild(grid, 0, 0, grid.length).
Walkthrough
The core of this method is a recursive function, let's call it build(row, col, size). This function is responsible for constructing the Quad Tree for the size x size sub-grid starting at (row, col).
The process within build(row, col, size) is as follows:
- Homogeneity Check: First, we determine if the sub-grid is homogeneous (contains all 0s or all 1s). We can do this by taking the value of the top-left cell,
grid[row][col], and then iterating through all the cells in thesize x sizesub-grid. If we find any cell with a different value, the grid is not homogeneous. - Leaf Node Creation: If the check passes (the grid is homogeneous), we create a new
Node. We setisLeaftotrue,valto the common value (true for 1, false for 0), and all four children pointers tonull. This node is then returned. - Internal Node Creation: If the check fails (the grid has mixed values), we create an internal node. We set
isLeaftofalseandvalto an arbitrary value (e.g.,true). Then, we divide the currentsize x sizegrid into four(size/2) x (size/2)sub-grids and make recursive calls tobuildfor each of these four sub-grids, assigning the returned nodes to thetopLeft,topRight,bottomLeft, andbottomRightchildren of the current internal node. This node is then returned.
The initial call to start the process would be build(0, 0, n), where n is the dimension of the input grid.
class Solution { public Node construct(int[][] grid) { return build(grid, 0, 0, grid.length); } private Node build(int[][] grid, int row, int col, int size) { // Check if the sub-grid is homogeneous boolean isHomogeneous = true; int firstVal = grid[row][col]; for (int i = row; i < row + size; i++) { for (int j = col; j < col + size; j++) { if (grid[i][j] != firstVal) { isHomogeneous = false; break; } } if (!isHomogeneous) { break; } } if (isHomogeneous) { return new Node(firstVal == 1, true, null, null, null, null); } else { int newSize = size / 2; Node topLeft = build(grid, row, col, newSize); Node topRight = build(grid, row, col + newSize, newSize); Node bottomLeft = build(grid, row + newSize, col, newSize); Node bottomRight = build(grid, row + newSize, col + newSize, newSize); // The 'val' for an internal node can be arbitrary, e.g., true. return new Node(true, false, topLeft, topRight, bottomLeft, bottomRight); } }}Complexity
Time
O(N^2 * log N). For each node in the Quad Tree, we iterate over its corresponding sub-grid. The recurrence relation is `T(N) = 4 * T(N/2) + O(N^2)`. At each level of recursion, the total work for the homogeneity check across all nodes at that level is `O(N^2)`. Since the depth of the tree is `log N`, the total time complexity is `O(N^2 * log N)`.
Space
O(N^2). The space is dominated by the storage for the Quad Tree itself, which can have `O(N^2)` nodes in the worst-case scenario (e.g., a checkerboard pattern). The recursion stack depth contributes `O(log N)`.
Trade-offs
Pros
Simple to understand and implement as it directly follows the problem definition.
Does not require any extra data structures for pre-computation.
Cons
Inefficient due to redundant computations. The homogeneity check for a sub-grid re-scans cells that were already scanned by its parent's check.
Solutions
Solution
/* // Definition for a QuadTree node. class Node { public boolean val; public boolean isLeaf; public Node topLeft; public Node topRight; public Node bottomLeft; public Node bottomRight; public Node() { this.val = false; this.isLeaf = false; this.topLeft = null; this.topRight = null; this.bottomLeft = null; this.bottomRight = null; } public Node(boolean val, boolean isLeaf) { this.val = val; this.isLeaf = isLeaf; this.topLeft = null; this.topRight = null; this.bottomLeft = null; this.bottomRight = null; } public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) { this.val = val; this.isLeaf = isLeaf; this.topLeft = topLeft; this.topRight = topRight; this.bottomLeft = bottomLeft; this.bottomRight = bottomRight; } }; */ class Solution { public Node construct ( int [][] grid ) { return dfs ( 0 , 0 , grid . length - 1 , grid [ 0 ]. length - 1 , grid ); } private Node dfs ( int a , int b , int c , int d , int [][] grid ) { int zero = 0 , one = 0 ; for ( int i = a ; i <= c ; ++ i ) { for ( int j = b ; j <= d ; ++ j ) { if ( grid [ i ][ j ] == 0 ) { zero = 1 ; } else { one = 1 ; } } } boolean isLeaf = zero + one == 1 ; boolean val = isLeaf && one == 1 ; Node node = new Node ( val , isLeaf ); if ( isLeaf ) { return node ; } node . topLeft = dfs ( a , b , ( a + c ) / 2 , ( b + d ) / 2 , grid ); node . topRight = dfs ( a , ( b + d ) / 2 + 1 , ( a + c ) / 2 , d , grid ); node . bottomLeft = dfs (( a + c ) / 2 + 1 , b , c , ( b + d ) / 2 , grid ); node . bottomRight = dfs (( a + c ) / 2 + 1 , ( b + d ) / 2 + 1 , c , d , grid ); return node ; } }Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.