LeetCode 3537 - Fill a Special Grid

The problem asks us to construct a structured matrix filled with all integers from to , arranged in a very specific recursive ordering constraint across quadrants. A grid is considered special if it satisfies two key properties.

LeetCode Problem 3537

Difficulty: 🟡 Medium
Topics: Array, Divide and Conquer, Matrix

Solution

Problem Understanding

The problem asks us to construct a structured $2^n \times 2^n$ matrix filled with all integers from $0$ to $4^n - 1$, arranged in a very specific recursive ordering constraint across quadrants.

A grid is considered special if it satisfies two key properties. First, there is a strict ordering between its four quadrants: every value in the top-right quadrant must be smaller than every value in the bottom-right quadrant, which in turn must be smaller than every value in the bottom-left quadrant, and finally all of those must be smaller than every value in the top-left quadrant. Second, each of these quadrants must itself recursively satisfy the same property, meaning the definition applies at every scale down to a $1 \times 1$ grid.

The input n determines the size of the grid as $2^n \times 2^n$, and the output is any valid special grid satisfying the constraints. The constraints are small (n <= 10), which implies up to a $1024 \times 1024$ grid, so an $O(4^n)$ solution is feasible.

The most important edge cases are when n = 0, where the grid is a single cell, and when n = 1, where we must correctly assign four distinct values in quadrant order. Another subtle point is ensuring global consistency of numbering across recursive quadrants, so that no overlaps occur and ordering constraints hold at every level.

Approaches

Brute Force Idea

A brute-force approach would attempt to assign numbers to the grid while enforcing the quadrant ordering constraints globally. One could imagine generating permutations or filling the grid cell by cell and validating constraints recursively.

However, this quickly becomes infeasible because the number of possible grids grows factorially with the number of cells. Even checking validity requires scanning quadrants repeatedly, making it computationally intractable for $n \ge 3$.

The key insight is that the constraints are self-similar and recursive. Each quadrant must itself be a special grid, and the ordering between quadrants is strictly hierarchical. This means we do not need search or backtracking; instead, we can construct the solution recursively by assigning disjoint numeric ranges to each quadrant.

At each level of recursion, we partition the value range into four consecutive blocks, assign each block to a quadrant, and recursively fill each quadrant using the same rule.

Approach Comparison

Approach Time Complexity Space Complexity Notes
Brute Force O((4^n)!) O(4^n) Attempts permutations or backtracking with constraint checking
Optimal Recursive Construction O(4^n) O(4^n) Divide grid into quadrants and assign contiguous value ranges recursively

Algorithm Walkthrough

The optimal solution is built using a divide-and-conquer strategy over the grid structure.

  1. We define a recursive function that constructs a special grid of size $2^k \times 2^k$. The base case occurs when $k = 0$, where the grid is simply $[[0]]$. This works because a $1 \times 1$ grid trivially satisfies all constraints.
  2. For a general level $k$, we first recursively construct a smaller special grid of size $2^{k-1} \times 2^{k-1}$. Let the side length of this smaller grid be $m$, so $m = 2^{k-1}$, and let the number of elements in it be $block = m \times m$.
  3. We then create a new grid of size $2m \times 2m$, which we will fill using four quadrants.
  4. We place four transformed copies of the smaller grid into the quadrants, but we assign different value offsets to ensure strict ordering:
  • Top-right quadrant receives values from the base grid plus offset $0$
  • Bottom-right quadrant receives values from the base grid plus offset $1 \cdot block$
  • Bottom-left quadrant receives values from the base grid plus offset $2 \cdot block$
  • Top-left quadrant receives values from the base grid plus offset $3 \cdot block$
  1. Each quadrant is filled by copying the smaller grid and adding the appropriate offset to every cell value.
  2. This process continues recursively until the entire grid is constructed.

Why it works

The correctness relies on two invariants. First, each recursive call produces a valid special grid for its quadrant. Second, the offsets ensure that all values in one quadrant are strictly smaller than all values in the next quadrant in the required ordering. Since the recursion preserves structure within each quadrant independently, the property holds at every level of the grid hierarchy.

Python Solution

from typing import List

class Solution:
    def specialGrid(self, n: int) -> List[List[int]]:
        def build(k: int) -> List[List[int]]:
            if k == 0:
                return [[0]]
            
            prev = build(k - 1)
            m = len(prev)
            block = m * m
            
            size = 2 * m
            res = [[0] * size for _ in range(size)]
            
            # top-right (0 offset)
            for i in range(m):
                for j in range(m):
                    res[i][j + m] = prev[i][j] + 0 * block
            
            # bottom-right (1 offset)
            for i in range(m):
                for j in range(m):
                    res[i + m][j + m] = prev[i][j] + 1 * block
            
            # bottom-left (2 offset)
            for i in range(m):
                for j in range(m):
                    res[i + m][j] = prev[i][j] + 2 * block
            
            # top-left (3 offset)
            for i in range(m):
                for j in range(m):
                    res[i][j] = prev[i][j] + 3 * block
            
            return res
        
        return build(n)

Code Explanation

The function build(k) constructs a valid grid for level k. The base case returns a single-cell grid. For larger values, it recursively constructs the smaller grid and then expands it into four quadrants.

Each quadrant is filled by copying the smaller grid and adding a fixed offset to ensure correct global ordering. The layout of indices ensures correct placement: top-right, bottom-right, bottom-left, and top-left.

Go Solution

func specialGrid(n int) [][]int {
    var build func(int) [][]int

    build = func(k int) [][]int {
        if k == 0 {
            return [][]int{{0}}
        }

        prev := build(k - 1)
        m := len(prev)
        block := m * m
        size := 2 * m

        res := make([][]int, size)
        for i := range res {
            res[i] = make([]int, size)
        }

        // top-right
        for i := 0; i < m; i++ {
            for j := 0; j < m; j++ {
                res[i][j+m] = prev[i][j]
            }
        }

        // bottom-right
        for i := 0; i < m; i++ {
            for j := 0; j < m; j++ {
                res[i+m][j+m] = prev[i][j] + block
            }
        }

        // bottom-left
        for i := 0; i < m; i++ {
            for j := 0; j < m; j++ {
                res[i+m][j] = prev[i][j] + 2*block
            }
        }

        // top-left
        for i := 0; i < m; i++ {
            for j := 0; j < m; j++ {
                res[i][j] = prev[i][j] + 3*block
            }
        }

        return res
    }

    return build(n)
}

Go-specific notes

The Go implementation mirrors the recursive structure used in Python. Slices are explicitly allocated to avoid nil references. Integer overflow is not a concern due to the constraint $n \le 10$, keeping values well within int range. The logic is identical, with explicit indexing replacing Python’s dynamic lists.

Worked Examples

Example 1: n = 0

The base case directly returns:

Step Grid
Base [[0]]

No recursion is triggered.

Example 2: n = 1

We build from n = 0.

Base grid:

[0]

Block size = 1.

Quadrant placement:

  • TR = 0 + 0 = 0
  • BR = 0 + 1 = 1
  • BL = 0 + 2 = 2
  • TL = 0 + 3 = 3

Final grid:

[3, 0]
[2, 1]

Example 3: n = 2

Start from n = 1 grid:

[3,0]
[2,1]

Block size = 4.

We replicate and offset:

Top-right (0-3):

[3,0]
[2,1]

Bottom-right (+4):

[7,4]
[6,5]

Bottom-left (+8):

[11,8]
[10,9]

Top-left (+12):

[15,12]
[14,13]

Final assembly:

[15,12,3,0]
[14,13,2,1]
[11,8,7,4]
[10,9,6,5]

Complexity Analysis

Measure Complexity Explanation
Time O(4^n) Each level constructs a full grid of size $4^n$, with constant work per cell
Space O(4^n) The grid itself dominates memory usage

The recursion produces one full grid per level, but each level’s work is proportional to the number of cells in the final grid, leading to linear work in the output size.

Test Cases

# Provided examples
assert Solution().specialGrid(0) == [[0]]
assert Solution().specialGrid(1) == [[3,0],[2,1]]
assert Solution().specialGrid(2) == [
    [15,12,3,0],
    [14,13,2,1],
    [11,8,7,4],
    [10,9,6,5]
]

# Edge case: smallest non-trivial recursion
assert len(Solution().specialGrid(1)) == 2

# Larger structure consistency check
g = Solution().specialGrid(3)
assert len(g) == 8 and len(g[0]) == 8

# Value range check
g = Solution().specialGrid(3)
flat = [x for row in g for x in row]
assert sorted(flat) == list(range(64))

# Structural sanity check
g = Solution().specialGrid(2)
assert g[0][0] > g[0][2]  # TL > TR
Test Why
n = 0 base case correctness
n = 1 minimal quadrant ordering validation
n = 2 full example structure correctness
n = 3 size check ensures exponential scaling correctness
sorted range check validates no missing/duplicate values

Edge Cases

One important edge case is n = 0, where the grid consists of a single cell. This case is critical because it anchors the recursion. If not handled explicitly, the recursive function would attempt to subdivide further and fail.

Another edge case is n = 1, where the quadrant ordering must be established for the first time. Any mistake in offset assignment or quadrant placement becomes immediately visible here because the grid is small enough to manually verify.

A third edge case is larger values like n = 10, where the grid size becomes 1024 by 1024. While recursion still works, inefficient copying or unnecessary recomputation would lead to performance issues. The implementation avoids this by ensuring each cell is written exactly once per recursion level in a controlled construction pattern.