You are given a n * n
matrix grid
containing only 0s and 1s. Your task is to represent this grid using a Quad-Tree and return the root of the constructed Quad-Tree. A Quad-Tree is a tree data structure where each internal node has exactly four children.
Each node in the Quad-Tree has two attributes:
val
: A boolean value representing whether the node represents a grid of 1s (True) or 0s (False).isLeaf
: A boolean value indicating whether the node is a leaf node (True) or an internal node with four children (False).The construction of the Quad-Tree follows these steps:
isLeaf
set to True and val
set to the value of the grid. Set the four children to Null.isLeaf
set to False and val
set to any value (it doesn't matter for internal nodes). Divide the current grid into four equal sub-grids (top-left, top-right, bottom-left, bottom-right).For example, consider the following input grid:
[[0, 1],
[1, 0]]
The corresponding Quad-Tree representation would be:
isLeaf = False, val = True
isLeaf = True, val = False
isLeaf = True, val = True
isLeaf = True, val = True
isLeaf = True, val = False
Another example:
[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]
In this case, the entire grid has the same value (all 1s), so the Quad-Tree would simply be a single leaf node:
isLeaf = True, val = True
Write a function that takes a n * n
grid as input and returns the root of the constructed Quad-Tree.
Given a n * n
matrix grid
of 0
s and 1
s only, the task is to represent the grid with a Quad-Tree and return the root of the Quad-Tree.
A Quad-Tree is a tree data structure in which each internal node has exactly four children. 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 a leaf node on the tree or False if the node has four children.The Quad-Tree is constructed as follows:
isLeaf
to True and val
to the value of the grid, and set the four children to Null.isLeaf
to False, set val
to any value, and divide the current grid into four sub-grids.The naive approach would be to recursively divide the grid into four sub-grids until we reach a sub-grid that contains only one value (either all 0s or all 1s). This approach directly follows the problem's recursive definition.
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':
n = len(grid)
def build_tree(grid, top_row, top_col, size):
if size == 1:
return Node(grid[top_row][top_col] == 1, True)
first_val = grid[top_row][top_col]
all_same = True
for i in range(top_row, top_row + size):
for j in range(top_col, top_col + size):
if grid[i][j] != first_val:
all_same = False
break
if not all_same:
break
if all_same:
return Node(first_val == 1, True)
half_size = size // 2
top_left = build_tree(grid, top_row, top_col, half_size)
top_right = build_tree(grid, top_row, top_col + half_size, half_size)
bottom_left = build_tree(grid, top_row + half_size, top_col, half_size)
bottom_right = build_tree(grid, top_row + half_size, top_col + half_size, half_size)
return Node(True, False, top_left, top_right, bottom_left, bottom_right)
return build_tree(grid, 0, 0, n)
O(N^2 log N), where N is the dimension of the grid. In the worst case, where the grid has alternating values, we might have to traverse each cell multiple times.
O(N^2) in the worst case, due to the recursive calls and the space required to store the Quad-Tree nodes. The maximum height of the Quad-Tree is log4(N^2) = log2(N).
The optimal approach improves upon the naive approach by minimizing redundant calculations. Since the core operation is checking if a region has the same value, caching/memoizing these results can significantly reduce computations.
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':
n = len(grid)
# Precompute prefix sums for efficient region sum calculation
prefix_sum = [[0] * (n + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, n + 1):
prefix_sum[i][j] = prefix_sum[i-1][j] + prefix_sum[i][j-1] - prefix_sum[i-1][j-1] + grid[i-1][j-1]
def region_sum(row1, col1, row2, col2):
# Calculate the sum of the region bounded by (row1, col1) and (row2, col2)
return prefix_sum[row2][col2] - prefix_sum[row1][col2] - prefix_sum[row2][col1] + prefix_sum[row1][col1]
def build_tree(top_row, top_col, size):
total = region_sum(top_row, top_col, top_row + size, top_col + size)
if total == 0:
return Node(False, True)
if total == size * size:
return Node(True, True)
half_size = size // 2
top_left = build_tree(top_row, top_col, half_size)
top_right = build_tree(top_row, top_col + half_size, half_size)
bottom_left = build_tree(top_row + half_size, top_col, half_size)
bottom_right = build_tree(top_row + half_size, top_col + half_size, half_size)
return Node(True, False, top_left, top_right, bottom_left, bottom_right)
return build_tree(0, 0, n)
O(N^2), where N is the dimension of the grid. Precomputing the prefix sum takes O(N^2) time. The tree construction then also takes O(N^2) time because each node is visited once.
O(N^2), primarily due to the prefix sum matrix. The Quad-Tree itself could also take up to O(N^2) space in the worst case.
None
.The optimal solution involves precomputing prefix sums to quickly determine if a sub-grid contains all 0s or all 1s. This reduces the time complexity from O(N^2 log N) to O(N^2), while maintaining the space complexity at O(N^2).