本文已参与「新人创作礼」活动,一起开启掘金创作之路。
| 每日一题做题记录,参考官方和三叶的题解 |
题目要求
理解
哦题目好长……
- 把矩阵递归四分,直到每个小块内的值相同(为叶子节点);
- 叶子节点,设为小块内的值(块内每个格子值相等);
- 非叶子节点,随便。
思路一:递归
- 递归判断当前矩阵范围内值是否都相等
- 相等则为叶子,返回
(值, true) - 不相等则从中间划分为四个,分别再判断
- 相等则为叶子,返回
- 可以直接用左上角点和右下角点的坐标来划定矩阵范围
- 划分时分别砍半横着、竖着
Java
class Solution {
int[][] g;
public Node construct(int[][] grid) {
g = grid;
int n = g.length;
return DFS(0, 0, n, n);
}
Node DFS(int tlx, int tly, int brx, int bry) {
boolean equ = true;
int t = g[tlx][tly];
// 块内所有值相等
for(int i = tlx; i < brx && equ; ++i)
for(int j = tly; j < bry && equ; ++j)
if(g[i][j] != t)
equ = false;
if(equ)
return new Node(t == 1, true); // 叶子
int nx = (brx + tlx) / 2, ny = (bry + tly) / 2; // 分割线
Node root = new Node(
true,
false,
DFS(tlx, tly, nx, ny),
DFS(tlx, ny, nx, bry),
DFS(nx, tly, brx, ny),
DFS(nx, ny, brx, bry)
);
return root;
}
}
- 时间复杂度:,单次递归调用4个子递归,其规模为,复杂度共;判断块内所有值相等复杂度为,最多拆分次(也是需判断次数),所以递归内复杂度为
- 空间复杂度:,忽略递归的额外空间开销
C++
class Solution {
vector<vector<int>> g;
public:
Node* construct(vector<vector<int>>& grid) {
g = grid;
int n = g.size();
return DFS(0, 0, n, n);
}
Node* DFS(int tlx, int tly, int brx, int bry) {
bool equ = true;
int t = g[tlx][tly];
// 块内所有值相等
for(int i = tlx; i < brx && equ; ++i)
for(int j = tly; j < bry && equ; ++j)
if(g[i][j] != t)
equ = false;
if(equ)
return new Node(t == 1, true); // 叶子
int nx = (brx + tlx) / 2, ny = (bry + tly) / 2; // 分割线
Node* root = new Node(
true,
false,
DFS(tlx, tly, nx, ny),
DFS(tlx, ny, nx, bry),
DFS(nx, tly, brx, ny),
DFS(nx, ny, brx, bry)
);
return root;
}
};
- 时间复杂度:,单次递归调用4个子递归,其规模为,复杂度共;判断块内所有值相等复杂度为,最多拆分次(也是需判断次数),所以递归内复杂度为
- 空间复杂度:,忽略递归的额外空间开销
思路二:递归+前缀和
- 前面判断块内所有值相等的暴力方法可以采用前缀和优化
- 记录内容其实为每一部分的面积,为或为则相同(为叶子),否则划分判断
Java
class Solution {
int[][] g;
int[][] pre = new int[70][70];
public Node construct(int[][] grid) {
g = grid;
int n = g.length;
// 前缀和
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + grid[i - 1][j - 1];
return DFS(0, 0, n, n);
}
Node DFS(int tlx, int tly, int brx, int bry) {
int tot = pre[brx][bry] - pre[brx][tly] - pre[tlx][bry] + pre[tlx][tly];
// 叶子
if(tot == 0)
return new Node(false, true);
else if(tot == (brx - tlx) * (bry - tly))
return new Node(true, true);
int nx = (tlx + brx) / 2, ny = (tly + bry) / 2; // 分割线
Node root = new Node(
true,
false,
DFS(tlx, tly, nx, ny),
DFS(tlx, ny, nx, bry),
DFS(nx, tly, brx, ny),
DFS(nx, ny, brx, bry)
);
return root;
}
}
- 时间复杂度:,递归复杂度和上面一样为,二维前缀和预处理复杂度也为;
递归内判断复杂度O(1),拆分O(log n)次【这部分是基于上一方法思路的个人胡乱分析,其实小于前面部分可以拿掉写成】 - 空间复杂度:,二维前缀和所需
C++
class Solution {
private:
vector<vector<int>> g;
vector<vector<int>> pre = vector<vector<int>> (70, vector<int>(70, 0));
public:
Node* construct(vector<vector<int>>& grid) {
g = grid;
int n = g.size();
// 前缀和
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + grid[i - 1][j - 1];
return DFS(0, 0, n, n);
}
Node* DFS(int tlx, int tly, int brx, int bry) {
int tot = pre[brx][bry] - pre[brx][tly] - pre[tlx][bry] + pre[tlx][tly];
// 叶子
if(tot == 0)
return new Node(false, true);
else if(tot == (brx - tlx) * (bry - tly))
return new Node(true, true);
int nx = (brx + tlx) / 2, ny = (bry + tly) / 2; // 分割线
Node* root = new Node(
true,
false,
DFS(tlx, tly, nx, ny),
DFS(tlx, ny, nx, bry),
DFS(nx, tly, brx, ny),
DFS(nx, ny, brx, bry)
);
return root;
}
};
- 时间复杂度:,递归复杂度和上面一样为,二维前缀和预处理复杂度也为;
递归内判断复杂度为O(1),拆分O(\log n)$次【这部分是基于上一方法思路的个人胡乱分析,其实小于前面部分可以拿掉写成】 - 空间复杂度:,二维前缀和所需
二维vector初始化
- 学习参考链接
- 格式为
vector<vector<int>> vec(n, vector<int>(m, 0));,为容器大小,为容器内每个值大小。 - 但是直接初始化全局变量会报错:
expected parameter declarator
- 意为编译器无法区分该语句是在声明变量还是函数,所以可以类比java的初始化方法,即
vector<vector<int>> vec = vector<vector<int>> (n, vector<int>(m, 0));- 其他解决方案参考这里
总结
递归应用题目,结束条件的+1、-1卡了一会,要理清思路看好是长度还是坐标。
| 欢迎指正与讨论! |