题目
标题和出处
标题:建立四叉树
出处:427. 建立四叉树
难度
5 级
题目描述
要求
给定一个 n × n \texttt{n} \times \texttt{n} n×n 的矩阵 grid \texttt{grid} grid,矩阵由若干 0 \texttt{0} 0 和 1 \texttt{1} 1 组成。要求用四叉树表示该矩阵 grid \texttt{grid} grid。
返回能表示 grid \texttt{grid} grid 的四叉树的根结点。
四叉树数据结构中,每个内部结点恰好有四个子结点。此外,每个结点都有两个属性:
- val \texttt{val} val:储存叶结点所代表的区域的值, 1 \texttt{1} 1 对应 true \texttt{true} true, 0 \texttt{0} 0 对应 false \texttt{false} false。
- isLeaf \texttt{isLeaf} isLeaf:当这个结点是叶结点时为 true \texttt{true} true,当这个结点有 4 \texttt{4} 4 个子结点时为 false \texttt{false} false。
注意,当 isLeaf \texttt{isLeaf} isLeaf 为 false \texttt{false} false 时,可以把 true \texttt{true} true 或者 false \texttt{false} false 赋值给 val \texttt{val} val,两种值都是允许的。
class Node {
public boolean val;
public boolean isLeaf;
public Node topLeft;
public Node topRight;
public Node bottomLeft;
public Node bottomRight;
}
按以下步骤为二维区域构建四叉树:
- 如果当前网格的值相同(即全为 0 \texttt{0} 0 或者全为 1 \texttt{1} 1),将 isLeaf \texttt{isLeaf} isLeaf 设为 true \texttt{true} true,将 val \texttt{val} val 设为网格相应的值,并将四个子结点都设为 null \texttt{null} null 然后停止。
- 如果当前网格的值不同,将 isLeaf \texttt{isLeaf} isLeaf 设为 false \texttt{false} false,将 val \texttt{val} val 设为任意值,然后如下图所示,将当前网格划分为四个子网格。
- 使用适当的子网格递归每个子结点。

四叉树格式
输出为使用层序遍历后四叉树的序列化形式,其中 null \texttt{null} null 表示路径终止符,其下面不存在结点。
它与二叉树的序列化非常相似。唯一的区别是结点以列表形式表示 [isLeaf, val] \texttt{[isLeaf, val]} [isLeaf, val]。
如果 isLeaf \texttt{isLeaf} isLeaf 或者 val \texttt{val} val 的值为 true \texttt{true} true,则表示它在列表 [isLeaf, val] \texttt{[isLeaf, val]} [isLeaf, val] 中的值为 1 \texttt{1} 1;如果 isLeaf \texttt{isLeaf} isLeaf 或者 val \texttt{val} val 的值为 false \texttt{false} false,则表示值为 0 \texttt{0} 0。
示例
示例 1:

输入:
grid
=
[[0,1],[1,0]]
\texttt{grid = [[0,1],[1,0]]}
grid = [[0,1],[1,0]]
输出:
[[0,1],[1,0],[1,1],[1,1],[1,0]]
\texttt{[[0,1],[1,0],[1,1],[1,1],[1,0]]}
[[0,1],[1,0],[1,1],[1,1],[1,0]]
解释:此示例的解释如下:
请注意,在下面四叉树的图示中,
0
\texttt{0}
0 表示
false
\texttt{false}
false,
1
\texttt{1}
1 表示
true
\texttt{true}
true。

示例 2:

输入:
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]]
\texttt{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]]}
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]]
输出:
[[0,1],[1,1],[0,1],[1,1],[1,0],null,null,null,null,[1,0],[1,0],[1,1],[1,1]]
\texttt{[[0,1],[1,1],[0,1],[1,1],[1,0],null,null,null,null,[1,0],[1,0],[1,1],[1,1]]}
[[0,1],[1,1],[0,1],[1,1],[1,0],null,null,null,null,[1,0],[1,0],[1,1],[1,1]]
解释:网格中的所有值都不相同。我们将网格划分为四个子网格。
topLeft
\texttt{topLeft}
topLeft、
bottomLeft
\texttt{bottomLeft}
bottomLeft 和
bottomRight
\texttt{bottomRight}
bottomRight 均具有相同的值。
topRight
\texttt{topRight}
topRight 具有不同的值,因此我们将其再分为
4
\texttt{4}
4 个子网格,这样每个子网格都具有相同的值。
解释如下图所示:

数据范围
- n = grid.length = grid[i].length \texttt{n} = \texttt{grid.length} = \texttt{grid[i].length} n=grid.length=grid[i].length
- n = 2 x \texttt{n} = \texttt{2}^\texttt{x} n=2x,其中 0 ≤ x ≤ 6 \texttt{0} \le \texttt{x} \le \texttt{6} 0≤x≤6
解法
思路和算法
根据定义,四叉树中的每个结点都对应一个矩阵,矩阵的行数和列数相同且为 2 2 2 的非负整数次幂,每个结点的情况如下。
-
如果一个结点对应的矩阵的边长等于 1 1 1,则该结点是叶结点,结点值为矩阵中的唯一元素值。
-
如果一个结点对应的矩阵的边长大于 1 1 1,则该结点可能是叶结点或非叶结点。如果该结点的四个子结点都是叶结点且值相同,则该结点是叶结点,否则该结点是非叶结点。
由于题目规定非叶结点的值可以任取,因此判断一个结点是否为叶结点时需要同时考虑其四个子结点是否为叶结点和四个子结点的值是否相同。此处将非叶结点的值设为其四个子结点的值的逻辑或运算的结果。
使用边长为 n n n 的矩阵 grid \textit{grid} grid 构造四叉树时,如果当前结点对应的矩阵边长大于 1 1 1,则首先构建当前结点的四个子结点,然后根据子结点的值构建当前结点。这是一个递归分治的过程。
分治的终止条件是 n = 1 n = 1 n=1,此时四叉树中唯一的结点是叶结点,结点值为矩阵中的唯一元素值。当 n > 1 n > 1 n>1 时,递归地构建当前结点的四个子结点,然后根据四个子结点构建当前结点,构建当前结点的做法如下。
-
如果四个子结点都是叶结点且值相同,则当前结点是叶结点,其值为四个子结点值的逻辑或运算的结果,将当前结点的子结点设为空。
-
否则,当前结点不是叶结点,其值为四个子结点值的逻辑或运算的结果,将四个子结点作为当前结点的四个子结点。
使用原始矩阵 grid \textit{grid} grid 根据上述做法构建四叉树,即可得到完整的四叉树。
代码
class Solution {
public Node construct(int[][] grid) {
return construct(grid, 0, 0, grid.length);
}
public Node construct(int[][] grid, int startRow, int startCol, int side) {
if (side == 1) {
return new Node(grid[startRow][startCol] == 1, true);
}
int halfSide = side / 2;
Node topLeft = construct(grid, startRow, startCol, halfSide);
Node topRight = construct(grid, startRow, startCol + halfSide, halfSide);
Node bottomLeft = construct(grid, startRow + halfSide, startCol, halfSide);
Node bottomRight = construct(grid, startRow + halfSide, startCol + halfSide, halfSide);
boolean val = topLeft.val || topRight.val || bottomLeft.val || bottomRight.val;
boolean isLeaf = topLeft.isLeaf && topRight.isLeaf && bottomLeft.isLeaf && bottomRight.isLeaf && topLeft.val == topRight.val && topLeft.val == bottomLeft.val && topLeft.val == bottomRight.val;
return isLeaf ? new Node(val, isLeaf) : new Node(val, isLeaf, topLeft, topRight, bottomLeft, bottomRight);
}
}
复杂度分析
-
时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是矩阵 grid \textit{grid} grid 的边长。分治的递归调用栈共有 O ( log n ) O(\log n) O(logn) 层,第 k k k 层调用时需要处理的结点数是 O ( 4 k ) O(4^k) O(4k),需要处理的结点总数是 O ( n 2 ) O(n^2) O(n2),每个结点的处理时间是 O ( 1 ) O(1) O(1),因此时间复杂度是 O ( n 2 ) O(n^2) O(n2)。
-
空间复杂度: O ( log n ) O(\log n) O(logn),其中 n n n 是矩阵 grid \textit{grid} grid 的边长。递归调用栈需要 O ( log n ) O(\log n) O(logn) 的空间。注意返回值不计入空间复杂度。
721

被折叠的 条评论
为什么被折叠?



