427. 建立四叉树
2023-06-29 本文已影响0人
红树_
参考427. 建立四叉树。
题目
给你一个 n * n
矩阵 grid
,矩阵由若干 0
和 1
组成。请你用四叉树表示该矩阵 grid
。
你需要返回能表示矩阵的 四叉树 的根结点。
注意,当 isLeaf
为 False 时,你可以把 True 或者 False 赋值给节点,两种值都会被判题机制 接受 。
四叉树数据结构中,每个内部节点只有四个子节点。此外,每个节点都有两个属性:
-
val
:储存叶子结点所代表的区域的值。1 对应 True,0 对应 False; -
isLeaf
: 当这个节点是一个叶子结点时为 True,如果它有 4 个子节点则为 False 。
class Node {
public boolean val;
public boolean isLeaf;
public Node topLeft;
public Node topRight;
public Node bottomLeft;
public Node bottomRight;
}
我们可以按以下步骤为二维区域构建四叉树:
- 如果当前网格的值相同(即,全为
0
或者全为1
),将isLeaf
设为 True ,将val
设为网格相应的值,并将四个子节点都设为 Null 然后停止。 - 如果当前网格的值不同,将
isLeaf
设为 False, 将val
设为任意值,然后如下图所示,将当前网格划分为四个子网格。 - 使用适当的子网格递归每个子节点。
四叉树格式:
输出为使用层序遍历后四叉树的序列化形式,其中null
表示路径终止符,其下面不存在节点。它与二叉树的序列化非常相似。唯一的区别是节点以列表形式表示[isLeaf, val]
。如果isLeaf
或者val
的值为 True ,则表示它在列表[isLeaf, val]
中的值为 1 ;如果isLeaf
或者val
的值为 False ,则表示值为 0 。
输入:grid = [[0,1],[1,0]]
输出:[[0,1],[1,0],[1,1],[1,1],[1,0]]
解释:0 表示 false,1 表示 True 。
输入: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]]
解释:网格中的所有值都不相同。我们将网格划分为四个子网格。
topLeft,bottomLeft 和 bottomRight 均具有相同的值。
topRight 具有不同的值,因此我们将其再分为 4 个子网格,这样每个子网格都具有相同的值。
解题思路
-
深度优先搜索:使用
dfs
递归地构建叶子结点。 - 深度优先搜索+二维前缀和:判断区域是否相同可以直接枚举所有区域值也可以使用二维前缀和求取二维区域和。
深度优先搜索
class Solution {
int[][] g;
public Node construct(int[][] grid) {
g = grid;
return build(0,0,grid.length);
}
//构造坐标(x,y)左上角长度为d的矩形为四叉树
Node build(int x,int y,int d) {
//检查区域是否值相同
int val = g[x][y];//默认值
boolean flag = true;
for(int i = x; i < x + d; i++) {
for(int j = y; j < y + d; j++) {
if(g[i][j] != val) {
flag = false;
break;
}
}
if(!flag) break;
}
Node node = new Node();
node.val = val==1;
node.isLeaf = flag;
if(!flag) {
node.topLeft = build(x,y,d/2);
node.topRight = build(x,y+d/2,d/2);
node.bottomLeft = build(x+d/2,y,d/2);
node.bottomRight = build(x+d/2,y+d/2,d/2);
}
return node;
}
}
复杂度分析
- 时间复杂度:
O(n^2logn)
,n
为矩阵行/列数,递归logn
,每次递归枚举遍历不超过n^2
。 - 空间复杂度:
O(logn)
,主要为递归栈空间。
深度优先搜索+二维前缀和
class Solution {
int[][] g;
int[][] preSum;
public Node construct(int[][] grid) {
g = grid;
preSum = new int[g.length+1][g.length+1];//二维前缀和
for(int i = 0; i < g.length; i++) {
for(int j = 0; j < g.length; j++) {
preSum[i+1][j+1]=preSum[i][j+1]+preSum[i+1][j]-preSum[i][j]+g[i][j];
}
}
return build(0,0,g.length);
}
//构造坐标(x,y)左上角长度为d的矩形为四叉树
Node build(int x,int y,int d) {
//检查区域是否值相同
int val = g[x][y];//默认值
boolean flag = val*d*d == (preSum[x+d][y+d]
-preSum[x][y+d]-preSum[x+d][y]+preSum[x][y]);
Node node = new Node();
node.val = val==1;
node.isLeaf = flag;
if(!flag) {
node.topLeft = build(x,y,d/2);
node.topRight = build(x,y+d/2,d/2);
node.bottomLeft = build(x+d/2,y,d/2);
node.bottomRight = build(x+d/2,y+d/2,d/2);
}
return node;
}
}
复杂度分析
- 时间复杂度:
O(n^2)
,前缀和判断O(1)
,递归logn
,预处理前缀和n^2
所以T(n^2+logn) = O(n^2)
。 - 空间复杂度:
O(n^2)
,主要为前缀和数组空间。
2023.06.30