Construct Quad Tree
Problem
Given a n * n matrix grid of 0's and 1's only. We want to represent the grid with a Quad-Tree.
Return the root of the Quad-Tree representing the grid.
Notice that you can assign the value of a node to True or False when isLeaf is False, and both are accepted in the answer.
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.isLeaf: True if the node is leaf node on the tree or False if the node has the 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:
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.
Examples
Example 1:
graph TD classDef orange fill:#FFA500,stroke:#333,stroke-width:2px; classDef white fill:#FFF,stroke:#333,stroke-width:2px; classDef blue fill:#00BFFF,stroke:#333,stroke-width:2px; classDef green fill:#32CD32,stroke:#333,stroke-width:2px; classDef root fill:#EEE,stroke:#333,stroke-width:2px; Root("Root<br>isLeaf:0<br>val:1") A("isLeaf:1<br>val:0") B("isLeaf:1<br>val:1") C("isLeaf:1<br>val:1") D("isLeaf:1<br>val:0") Root -- Top Left --> A Root -- Top Right --> B Root -- Bottom Left --> C Root -- Bottom Right --> D class A orange; class B white; class C blue; class D green; class Root root;
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 represnts False and 1 represents True in the photo representing the Quad-Tree.
Example 2:
graph TD classDef orange fill:#FFA500,stroke:#333,stroke-width:2px; classDef blue fill:#00BFFF,stroke:#333,stroke-width:2px; classDef green fill:#32CD32,stroke:#333,stroke-width:2px; classDef root fill:#EEE,stroke:#333,stroke-width:2px; classDef yellow fill:#FFFF99,stroke:#333,stroke-width:2px; classDef lightgray fill:#D3D3D3,stroke:#333,stroke-width:2px; classDef lightpurple fill:#E6CCFF,stroke:#333,stroke-width:2px; classDef lightgreen fill:#90EE90,stroke:#333,stroke-width:2px; classDef white fill:#FFF,stroke:#333,stroke-width:2px; Root("Root<br>isLeaf:0<br>val:1") TL("isLeaf:1<br>val:1") TR("isLeaf:0<br>val:1") BL("isLeaf:1<br>val:1") BR("isLeaf:1<br>val:0") TR_TL("isLeaf:1<br>val:0") TR_TR("sLeaf:1<br>val:0") TR_BL("isLeaf:1<br>val:1") TR_BR("isLeaf:1<br>val:1") Root -- Top Left --> TL Root -- Top Right --> TR Root -- Bottom Left --> BL Root -- Bottom Right --> BR TR -- Top Left --> TR_TL TR -- Top Right --> TR_TR TR -- Bottom Left --> TR_BL TR -- Bottom Right --> TR_BR class Root root; class TL orange; class BL blue; class BR green; class TR white; class TR_TL yellow; class TR_TR lightgray; class TR_BL lightpurple; class TR_BR lightgreen;
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:
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;
}
}
Solution
Method 1 – Recursive Divide and Conquer (Quad Tree Construction)
Intuition
The main idea is to recursively divide the grid into four quadrants until each quadrant contains only a single value (all 0s or all 1s). At each step, we check if the current subgrid is uniform. If it is, we create a leaf node; otherwise, we continue dividing. This approach leverages the quad tree's structure, where each node represents a region of the grid, and leaf nodes represent uniform regions.
Approach
- Define a recursive function that takes the current subgrid boundaries (
rowStart,colStart,rowEnd,colEnd). - Check if all values in the current subgrid are the same:
- If yes, return a leaf node with
isLeaf = trueandvalset to the value of the subgrid. - If not, divide the subgrid into four equal parts (top left, top right, bottom left, bottom right).
- If yes, return a leaf node with
- Recursively construct each of the four children using the corresponding subgrid boundaries.
- Return a non-leaf node with
isLeaf = falseand the four children attached. - Start the recursion with the entire grid.
Complexity
- ⏰ Time complexity:
O(n^2 log n)in the worst case, as each level divides the grid and checks all cells in the region. - 🧺 Space complexity:
O(n^2)for the tree structure and recursion stack.
Code
C++
class Node {
public:
bool val;
bool isLeaf;
Node *topLeft, *topRight, *bottomLeft, *bottomRight;
Node() : val(false), isLeaf(false), topLeft(nullptr), topRight(nullptr), bottomLeft(nullptr), bottomRight(nullptr) {}
Node(bool v, bool l) : val(v), isLeaf(l), topLeft(nullptr), topRight(nullptr), bottomLeft(nullptr), bottomRight(nullptr) {}
Node(bool v, bool l, Node* tl, Node* tr, Node* bl, Node* br) : val(v), isLeaf(l), topLeft(tl), topRight(tr), bottomLeft(bl), bottomRight(br) {}
};
class Solution {
public:
Node* construct(vector<vector<int>>& grid) {
return build(grid, 0, 0, grid.size() - 1, grid[0].size() - 1);
}
Node* build(vector<vector<int>>& grid, int r1, int c1, int r2, int c2) {
bool isLeaf = true;
int val = grid[r1][c1];
for (int i = r1; i <= r2 && isLeaf; ++i)
for (int j = c1; j <= c2; ++j)
if (grid[i][j] != val) { isLeaf = false; break; }
if (isLeaf) return new Node(val == 1, true);
int rm = (r1 + r2) / 2, cm = (c1 + c2) / 2;
return new Node(false, false,
build(grid, r1, c1, rm, cm),
build(grid, r1, cm + 1, rm, c2),
build(grid, rm + 1, c1, r2, cm),
build(grid, rm + 1, cm + 1, r2, c2));
}
};
Go
type Node struct {
Val bool
IsLeaf bool
TopLeft *Node
TopRight *Node
BottomLeft *Node
BottomRight *Node
}
func construct(grid [][]int) *Node {
var build func(int, int, int, int) *Node
build = func(r1, c1, r2, c2 int) *Node {
val := grid[r1][c1]
isLeaf := true
for i := r1; i <= r2 && isLeaf; i++ {
for j := c1; j <= c2; j++ {
if grid[i][j] != val {
isLeaf = false
break
}
}
}
if isLeaf {
return &Node{Val: val == 1, IsLeaf: true}
}
rm, cm := (r1+r2)/2, (c1+c2)/2
return &Node{
Val: false, IsLeaf: false,
TopLeft: build(r1, c1, rm, cm),
TopRight: build(r1, cm+1, rm, c2),
BottomLeft: build(rm+1, c1, r2, cm),
BottomRight: build(rm+1, cm+1, r2, c2),
}
}
n := len(grid)
return build(0, 0, n-1, n-1)
}
Java
class Solution {
public Node construct(int[][] grid) {
return build(grid, 0, 0, grid.length - 1, grid[0].length - 1);
}
private Node build(int[][] grid, int rowStart, int colStart, int rowEnd, int colEnd) {
boolean isLeaf = true;
int val = grid[rowStart][colStart];
for (int i = rowStart; i <= rowEnd && isLeaf; i++) {
for (int j = colStart; j <= colEnd; j++) {
if (grid[i][j] != val) {
isLeaf = false;
break;
}
}
}
if (isLeaf) {
return new Node(val == 1, true, null, null, null, null);
}
int rowMid = (rowStart + rowEnd) / 2;
int colMid = (colStart + colEnd) / 2;
Node topLeft = build(grid, rowStart, colStart, rowMid, colMid);
Node topRight = build(grid, rowStart, colMid + 1, rowMid, colEnd);
Node bottomLeft = build(grid, rowMid + 1, colStart, rowEnd, colMid);
Node bottomRight = build(grid, rowMid + 1, colMid + 1, rowEnd, colEnd);
return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight);
}
}
Python
class Node:
def __init__(self, val, isLeaf, topLeft=None, topRight=None, bottomLeft=None, bottomRight=None):
self.val = val
self.isLeaf = isLeaf
self.topLeft = topLeft
self.topRight = topRight
self.bottomLeft = bottomLeft
self.bottomRight = bottomRight
class Solution:
def construct(self, grid: list[list[int]]) -> Node:
def build(r1, c1, r2, c2):
val = grid[r1][c1]
isLeaf = all(grid[i][j] == val for i in range(r1, r2+1) for j in range(c1, c2+1))
if isLeaf:
return Node(val == 1, True)
rm, cm = (r1 + r2) // 2, (c1 + c2) // 2
return Node(False, False,
build(r1, c1, rm, cm),
build(r1, cm+1, rm, c2),
build(rm+1, c1, r2, cm),
build(rm+1, cm+1, r2, c2))
n = len(grid)
return build(0, 0, n-1, n-1)
Kotlin
class Node(
var `val`: Boolean,
var isLeaf: Boolean,
var topLeft: Node? = null,
var topRight: Node? = null,
var bottomLeft: Node? = null,
var bottomRight: Node? = null
)
class Solution {
fun construct(grid: Array<IntArray>): Node? {
fun build(r1: Int, c1: Int, r2: Int, c2: Int): Node? {
val v = grid[r1][c1]
var isLeaf = true
for (i in r1..r2) for (j in c1..c2) if (grid[i][j] != v) { isLeaf = false; break }
if (isLeaf) return Node(v == 1, true)
val rm = (r1 + r2) / 2
val cm = (c1 + c2) / 2
return Node(false, false,
build(r1, c1, rm, cm),
build(r1, cm+1, rm, c2),
build(rm+1, c1, r2, cm),
build(rm+1, cm+1, r2, c2))
}
val n = grid.size
return build(0, 0, n-1, n-1)
}
}
TypeScript
class Node {
val: boolean;
isLeaf: boolean;
topLeft: Node | null;
topRight: Node | null;
bottomLeft: Node | null;
bottomRight: Node | null;
constructor(val: boolean, isLeaf: boolean, topLeft?: Node | null, topRight?: Node | null, bottomLeft?: Node | null, bottomRight?: Node | null) {
this.val = val;
this.isLeaf = isLeaf;
this.topLeft = topLeft ?? null;
this.topRight = topRight ?? null;
this.bottomLeft = bottomLeft ?? null;
this.bottomRight = bottomRight ?? null;
}
}
function construct(grid: number[][]): Node | null {
function build(r1: number, c1: number, r2: number, c2: number): Node {
const val = grid[r1][c1];
let isLeaf = true;
for (let i = r1; i <= r2 && isLeaf; i++)
for (let j = c1; j <= c2; j++)
if (grid[i][j] !== val) { isLeaf = false; break; }
if (isLeaf) return new Node(val === 1, true);
const rm = Math.floor((r1 + r2) / 2), cm = Math.floor((c1 + c2) / 2);
return new Node(false, false,
build(r1, c1, rm, cm),
build(r1, cm+1, rm, c2),
build(rm+1, c1, r2, cm),
build(rm+1, cm+1, r2, c2));
}
const n = grid.length;
return build(0, 0, n-1, n-1);
}
Rust
pub struct Node {
pub val: bool,
pub is_leaf: bool,
pub top_left: Option<Box<Node>>,
pub top_right: Option<Box<Node>>,
pub bottom_left: Option<Box<Node>>,
pub bottom_right: Option<Box<Node>>,
}
impl Node {
pub fn new(val: bool, is_leaf: bool) -> Self {
Node { val, is_leaf, top_left: None, top_right: None, bottom_left: None, bottom_right: None }
}
pub fn with_children(val: bool, is_leaf: bool, tl: Node, tr: Node, bl: Node, br: Node) -> Self {
Node {
val,
is_leaf,
top_left: Some(Box::new(tl)),
top_right: Some(Box::new(tr)),
bottom_left: Some(Box::new(bl)),
bottom_right: Some(Box::new(br)),
}
}
}
pub fn construct(grid: Vec<Vec<i32>>) -> Option<Box<Node>> {
fn build(grid: &Vec<Vec<i32>>, r1: usize, c1: usize, r2: usize, c2: usize) -> Box<Node> {
let val = grid[r1][c1];
let mut is_leaf = true;
for i in r1..=r2 {
for j in c1..=c2 {
if grid[i][j] != val { is_leaf = false; break; }
}
if !is_leaf { break; }
}
if is_leaf {
return Box::new(Node::new(val == 1, true));
}
let rm = (r1 + r2) / 2;
let cm = (c1 + c2) / 2;
Box::new(Node::with_children(false, false,
*build(grid, r1, c1, rm, cm),
*build(grid, r1, cm+1, rm, c2),
*build(grid, rm+1, c1, r2, cm),
*build(grid, rm+1, cm+1, r2, c2)))
}
let n = grid.len();
Some(build(&grid, 0, 0, n-1, n-1))
}