Python 0427.py
class Solution:
def construct(self, grid: List[List[int]]) -> 'Node':
size = len(grid)
height = int(log2(size))
def build(x, y, level):
if level == 0:
return Node(grid[y][x], True, None, None, None, None)
step = 2**(level - 1)
a = build(x, y, level-1)
b = build(x+step, y, level-1)
c = build(x, y+step, level-1)
d = build(x+step, y+step, level-1)
if a.isLeaf and b.isLeaf and c.isLeaf and d.isLeaf and a.val == b.val and b.val == c.val and c.val == d.val:
return Node(a.val, True, None, None, None, None)
else:
return Node(a.val, False, a, b, c, d)
return build(0, 0, height)
Difficulty
and has an acceptance rate of about 79% (200k+ submissions).
Typical patterns to solve this problem:
- Array
- Divide And Conquer
- Matrix
- Tree
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation builds a quad-tree representation of a square binary matrix. The construct method receives a two-dimensional list grid whose side length is a power of two. It computes the height of the tree from the matrix size, then recursively partitions the matrix into four equal quadrants until reaching individual cells. At each recursion level the function decides whether the four child nodes can be merged into a single leaf node; otherwise it creates an internal node that references the four children.
Key Steps
Determining the tree height
size = len(grid)
height = int(log2(size))
The side length of the matrix is stored in size. Because the matrix size is guaranteed to be a power of two, taking the base-2 logarithm yields the number of subdivision levels required to reach single cells. The result is stored in height and passed to the recursive builder.
Recursive construction function
def build(x, y, level):
if level == 0:
return Node(grid[y][x], True, None, None, None, None)
step = 2**(level - 1)
a = build(x, y, level-1)
b = build(x+step, y, level-1)
c = build(x, y+step, level-1)
d = build(x+step, y+step, level-1)
if a.isLeaf and b.isLeaf and c.isLeaf and d.isLeaf and a.val == b.val and b.val == c.val and c.val == d.val:
return Node(a.val, True, None, None, None, None)
else:
return Node(a.val, False, a, b, c, d)
The function build receives the top-left coordinates (x, y) of the current sub-grid and the remaining level count. When level reaches zero the sub-grid corresponds to a single matrix element; a leaf node is created directly from that value.
For higher levels the side length of the current sub-grid is 2**level. The variable step equals half of that length, allowing the function to compute the origins of the four quadrants: top-left (a), top-right (b), bottom-left (c), and bottom-right (d). Each quadrant is processed by a recursive call with level-1.
After the four child nodes are obtained, the code checks whether all four are leaves and share the same boolean value. If that condition holds, the four leaves are collapsed into a single leaf node whose value matches the children. Otherwise an internal node is created; its isLeaf flag is set to False and it stores references to the four child nodes. The value stored in an internal node is taken from the first child (a.val), which is permissible because the value field is irrelevant for non-leaf nodes.
Initiating the recursion
return build(0, 0, height)
The construction starts at the origin of the matrix with the full height, producing the root of the quad-tree.
Edge Cases Handled
When the input matrix consists of a single element (size == 1), height becomes zero. The recursive call build(0, 0, 0) immediately triggers the base case, returning a leaf node that directly reflects the sole matrix value. This path avoids any further subdivision and correctly represents the trivial quad-tree.
If an entire quadrant contains uniform values, the recursive descent still creates leaf nodes for each cell, but the subsequent merging condition collapses them into a single leaf node for that quadrant. This behavior ensures that large homogeneous regions are represented compactly, regardless of their position in the matrix.
The algorithm also gracefully handles matrices where uniformity appears only at deeper levels. For example, a quadrant may consist of a mixture of values that only become uniform after further subdivision; the recursion continues until the base case, then merges only when the four children satisfy the uniformity test. This guarantees correct representation of any pattern of 0s and 1s.
Performance Optimizations
The recursion computes the size of each quadrant using step = 2**(level - 1). Because the exponentiation is performed with integer powers of two, the operation is effectively a bit shift, which is computationally cheap. The algorithm avoids recomputing the size of sub-grids from coordinates; it derives the step directly from the current recursion depth.
Merging of homogeneous leaf nodes reduces the total number of nodes created. Instead of constructing a full tree with 4^height leaf nodes, the code collapses any region where all four children are identical into a single node. This pruning dramatically cuts memory usage for matrices with large uniform blocks.
The base case returns a node without allocating child references (None for all four pointers). This minimizes the memory footprint for leaf nodes, as they do not carry unnecessary references.
All recursive calls are tail-independent; each call creates exactly four child calls and then decides on merging. The recursion depth equals the height of the tree, which is log2(size). Consequently, the call stack grows only proportionally to the logarithm of the matrix dimension, keeping stack usage modest even for large inputs.
ELI5 - Simplified Explanation
Imagine you have a big square puzzle made of many tiny colored blocks. This computer code acts like a very smart puzzle maker that tries to pack the picture into the smallest box possible. It starts by looking at the whole big square and asking if every single block inside is the exact same color. If all the blocks match, it just uses one big piece to represent the whole area. If the blocks are a mix of colors, it cuts the big square into four smaller equal squares. Then it runs the exact same rule on those smaller squares, checking if they can be individual solid colors or if they need to be cut again. It keeps doing this over and over until every part of the big puzzle is neatly packed using either one solid piece or a group of four smaller pieces. This is a clever way to save space while still remembering exactly what the original picture looks like.
Code Quality
Complexity Analysis
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | ||||||||
|---|---|---|---|---|---|---|---|---|
| author | solutions | |||||||
| www | AlgoMonster | Java | C++ | Python | TS | |||
| WalkCCC | Java | C++ | Python | |||||
| leetcode.ca | Java | C++ | Python | Go | ||||
| LeetcodeHelp | Java | C++ | Python | |||||
| Progiez | Java | C++ | Python | |||||
| github | BigEggStudy | C# | ||||||
| black-shadows | C++ | |||||||
| charles-wangkai | Java | |||||||
| everthis | JS | |||||||
| fsq | C++ | |||||||
| JoshCrozier | JS | |||||||
| lkwq007 | Python | |||||||
| suxrobGM | C# | |||||||
| varunu28 | Java | |||||||
| vthwang | Java | |||||||
| AlgoMonster | C++, Python, Java, TypeScript | |||||||
| WalkCCC | C++, Python, Java | |||||||
| leetcode.ca | C++, Go, Java, Python | |||||||
| LeetcodeHelp | C++, Java, Python | |||||||
| Progiez | C++, Java, Python | |||||||
| BigEggStudy | C# | |||||||
| black-shadows | C++, C++ | |||||||
| charles-wangkai | Java | |||||||
| everthis | JavaScript | |||||||
| fsq | C++ | |||||||
| JoshCrozier | JavaScript | |||||||
| lkwq007 | Python | |||||||
| suxrobGM | C# | |||||||
| varunu28 | Java | |||||||
| vthwang | Java |
Videos
Solved On The Same Day
- problem 110: Balanced Binary Tree
- problem 111: Minimum Depth of Binary Tree
- problem 118: Pascal's Triangle
- problem 119: Pascal's Triangle II
- problem 228: Summary Ranges
- problem 530: Minimum Absolute Difference in BST
- problem 82: Remove Duplicates from Sorted List II
- problem 102: Binary Tree Level Order Traversal
- problem 103: Binary Tree Zigzag Level Order Traversal
- problem 105: Construct Binary Tree from Preorder and Inorder Traversal
- problem 106: Construct Binary Tree from Inorder and Postorder Traversal
- problem 107: Binary Tree Level Order Traversal II
- problem 109: Convert Sorted List to Binary Search Tree
- problem 117: Populating Next Right Pointers in Each Node II
- problem 128: Longest Consecutive Sequence
- problem 138: Copy List with Random Pointer
- problem 150: Evaluate Reverse Polish Notation
- problem 155: Min Stack
- problem 209: Minimum Size Subarray Sum
- problem 274: H-Index
- problem 380: Insert Delete GetRandom O(1)
- problem 452: Minimum Number of Arrows to Burst Balloons
- problem 30: Substring with Concatenation of All Words
- problem 37: Sudoku Solver
- problem 44: Wildcard Matching
- problem 1326: Minimum Number of Taps to Open to Water a Garden
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality