

文章目录
-
- 摘要
- 描述
- 题解答案(简要思路)
- [题解代码分析(含可运行 Demo)](#题解代码分析(含可运行 Demo))
- [Swift 可运行代码](#Swift 可运行代码)
- 代码解析(逐段讲解)
-
- [1. 为什么要用 `build(x, y, size)` 这种递归形式?](#1. 为什么要用
build(x, y, size)这种递归形式?) - [2. 判断区域是否统一](#2. 判断区域是否统一)
- [3. 构建内部节点](#3. 构建内部节点)
- [1. 为什么要用 `build(x, y, size)` 这种递归形式?](#1. 为什么要用
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
四叉树(QuadTree)这个结构在日常开发中并不常见,但它其实在图像处理、地图分块、区域搜索等领域非常常用。这道题让我们根据一个 n × n 的 0/1 网格去构建对应的四叉树结构。
核心思想其实就是一句话:
如果区域内所有值相同,就是叶子节点;否则,把区域继续分成四块,递归构建。
虽然概念简单,但写代码时容易在"坐标"和"区域分割"上打结。本文会用口语化的方式带你拆清楚每一步,并提供一个可运行的 Swift Demo,让你完全掌握四叉树构建的套路。

描述
题目给了我们一个正方形矩阵 grid,里面只有 0 和 1。我们需要用"四叉树"(QuadTree)来表示这块区域。
每个节点有两个关键属性:
isLeaf:是否是叶子节点val:叶子节点的值(0/1 对应 false/true)
如果当前区域全是一样的数字,比如全是 1 或全是 0,那就直接生成一个叶子节点。
如果区域内包含 0 和 1 混合,那就把区域划分成四个子区域:
txt
topLeft topRight
bottomLeft bottomRight
然后对四块区域分别递归构建四叉树。
最终返回四叉树的根节点。
题解答案(简要思路)
解决这题的核心流程如下:
-
编写一个函数
build(x, y, size)它用于构建以
(x, y)为左上角、边长为size的区域。 -
在这个区域里检查是否全为相同的数:
- 如果是:直接返回叶子节点
- 如果不是:继续分为四块
-
递归构建四个子节点
-
返回内部节点
常见容易出错的点:
- 区域分割的坐标计算是否正确
- 递归终止条件是否写对
- 数组访问是否越界
- 节点的
val在非叶节点时无所谓,但习惯设为true/false均可
下面我们直接进入完整 Swift Demo,之后再做详细拆解。

题解代码分析(含可运行 Demo)
下面这段代码可以直接运行,包括:
- Node 定义(Swift 版本)
- 构建四叉树的递归逻辑
- 工具函数:打印树结构(方便调试)
- 示例测试代码
Swift 可运行代码
swift
import Foundation
class Node {
var val: Bool
var isLeaf: Bool
var topLeft: Node?
var topRight: Node?
var bottomLeft: Node?
var bottomRight: Node?
init(_ val: Bool, _ isLeaf: Bool) {
self.val = val
self.isLeaf = isLeaf
}
}
class Solution {
func construct(_ grid: [[Int]]) -> Node? {
return build(grid, 0, 0, grid.count)
}
private func build(_ grid: [[Int]], _ x: Int, _ y: Int, _ size: Int) -> Node {
// 判断当前区域是否统一
if isUniform(grid, x, y, size) {
let val = grid[x][y] == 1
return Node(val, true)
}
// 区域不统一,继续拆分
let half = size / 2
let topLeft = build(grid, x, y, half)
let topRight = build(grid, x, y + half, half)
let bottomLeft = build(grid, x + half, y, half)
let bottomRight = build(grid, x + half, y + half, half)
let node = Node(true, false) // val 无所谓
node.topLeft = topLeft
node.topRight = topRight
node.bottomLeft = bottomLeft
node.bottomRight = bottomRight
return node
}
private func isUniform(_ grid: [[Int]], _ x: Int, _ y: Int, _ size: Int) -> Bool {
let first = grid[x][y]
for i in x..<x+size {
for j in y..<y+size {
if grid[i][j] != first {
return false
}
}
}
return true
}
}
// MARK: 工具:打印四叉树(简单文本结构)
func printQuadTree(_ node: Node?, _ indent: String = "") {
guard let node else { return }
if node.isLeaf {
print("\(indent)Leaf: \(node.val ? 1 : 0)")
} else {
print("\(indent)Node")
printQuadTree(node.topLeft, indent + " ")
printQuadTree(node.topRight, indent + " ")
printQuadTree(node.bottomLeft, indent + " ")
printQuadTree(node.bottomRight, indent + " ")
}
}
代码解析(逐段讲解)
1. 为什么要用 build(x, y, size) 这种递归形式?
因为我们每次处理的都是 grid 的子矩阵 ,所以需要用左上角坐标 (x, y) 和区域大小 size 来定位子区域。
这样分割区域时非常清晰:
txt
当前区域:(x, y), size
四个子区域:
topLeft: (x, y), size/2
topRight: (x, y+half)
bottomLeft: (x+half, y)
bottomRight: (x+half, y+half)
不用重新创建子矩阵,效率更高。
2. 判断区域是否统一
swift
private func isUniform(_ grid: [[Int]], _ x: Int, _ y: Int, _ size: Int) -> Bool
这个函数非常关键,它检查该区域是否全 0 或全 1。
如果统一,那这个区域就可以变成一个叶子节点。
在构建 quadtree 的过程中,这一步是性能瓶颈之一,但输入规模较小(最大 64 × 64),所以完全没问题。
3. 构建内部节点
如果不是叶子节点:
swift
let node = Node(true, false)
这里 val 随便设,因为内部节点的 val 不影响结果(判题机制就是这么设计的)。
然后把四个子节点挂上去:
swift
node.topLeft = topLeft
node.topRight = topRight
node.bottomLeft = bottomLeft
node.bottomRight = bottomRight
流程非常自然。
示例测试及结果
我们用一个简单的例子来测试:
txt
grid = [
[1, 1],
[1, 1]
]
测试代码:
swift
let grid = [
[1, 1],
[1, 1]
]
let solution = Solution()
let root = solution.construct(grid)
printQuadTree(root)
输出:
txt
Leaf: 1
因为整块区域都是 1,所以整个树只有一个节点。
再试另一个比较复杂的:
txt
grid = [
[0,1],
[1,0]
]
结果大概是:
txt
Node
Leaf: 0
Leaf: 1
Leaf: 1
Leaf: 0
表示四个区域分别为 0、1、1、0。
时间复杂度
对于每一级,我们会对某个区域扫描一遍判断是否统一。
区域大小会依次为:
txt
n^2 + 4 * (n/2)^2 + 16 * (n/4)^2 + ...
这个序列的和是:
O(n²)
因为所有区域总和面积就是 grid 面积。
所以:
- 时间复杂度:O(n²)
非常合理。
空间复杂度
主要是递归栈和树结构本身。
- 树的节点数量最多是:
四叉树最多节点数 = O(n²)(最坏情况每个单元格都是一个叶子) - 递归深度为 log n
所以整体空间复杂度:
- O(n²)
总结
这题的核心就是熟悉四叉树的定义,然后用递归自然地实现整棵树。
关键点回顾:
- 区域统一 → 叶子节点
- 区域不统一 → 四块继续递归
- 坐标 / size 控制是整题的重要细节
- 内部节点的 val 无影响,但要设置 isLeaf = false
掌握这个模式后,你会发现四叉树其实不难,它和你平时熟悉的"递归分治"套路非常一致,只是"从二叉树变成了四叉树"而已。