一、知识点介绍
二、练习题目
(1) 面试题 01.08. 零矩阵
(2) 73. 矩阵置零
(3) 1727. 重新排列后的最大子矩阵
(4) 1034. 边界着色
三、算法思路
1. 面试题 01.08. 零矩阵
(1) 遍历题目给的矩阵,记录下值为0的矩阵的横纵坐标;
(2)再遍历一遍,把相应的列和行的元素置为0。
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
vector<int> column;
vector<int> row;
int i, j;
for(i = 0; i < matrix.size(); ++i) {
for(j = 0; j < matrix[i].size(); ++j) {
if(matrix[i][j] == 0) {
column.push_back(i);
row.push_back(j);
}
}
}
for(i = 0; i < column.size(); ++i) {
for(j = 0; j < matrix[column[i]].size(); ++j) {
matrix[column[i]][j] = 0;
}
}
for(i = 0; i < row.size(); ++i) {
for(j = 0; j < matrix.size(); ++j) {
matrix[j][row[i]] = 0;
}
}
}
};
2. 矩阵置零
(1) 跟上面那道题一样,copy过来就好了;
(2) 也可以先复制一个跟原矩阵一样的矩阵temp。再遍历矩阵temp,找到为0的值,把题目给的矩阵的相应的行和列的值设置为0.
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
vector<int> column;
vector<int> row;
int i, j;
for(i = 0; i < matrix.size(); ++i) {
for(j = 0; j < matrix[i].size(); ++j) {
if(matrix[i][j] == 0) {
column.push_back(i);
row.push_back(j);
}
}
}
for(i = 0; i < column.size(); ++i) {
for(j = 0; j < matrix[column[i]].size(); ++j) {
matrix[column[i]][j] = 0;
}
}
for(i = 0; i < row.size(); ++i) {
for(j = 0; j < matrix.size(); ++j) {
matrix[j][row[i]] = 0;
}
}
}
};
3. 重新排列后的最大子矩阵
(1) 先预处理数据,具体做法是:把每一行都当作水平线,统计向上衍生的值为1的方块的个数
(2) 然后再把每一列的个数赋值给新的数组,并且对数组的大小进行升序排序
(3) 当j指到某一点的时候,如第三张图所示,最大面积为蓝色框住的面积,再更新ans的值,并返回。
class Solution {
public:
int largestSubmatrix(vector<vector<int>>& matrix) {
int row = matrix.size();
int col = matrix[0].size();
int i, j, ans = 0;
int vline[100010];
int tvline[100010];
memset(vline, 0, sizeof(vline));
for(i = 0; i < row; ++i) {
for(j = 0; j < col; ++j) {
if(matrix[i][j] == 0) {
vline[j] = 0;
} else {
vline[j]++;
}
}
for(j = 0; j < col; ++j) {
tvline[j] = vline[j];
}
sort(tvline, tvline+col);
for(j = 0; j < col; ++j) {
ans = max(ans, tvline[j]*(col-j));
}
}
return ans;
}
};
4. 边界着色
(1) 这道题是关于深度优先搜索的。
(2) 题目已经给了搜索起点,就是grid[row][col], 从起点开始递归搜索,确定哪些是连通的点。并用一个hash数组进行标记。
(3) 有四个方向,通过加上dir的值,改变点的位置。
(4) 递归搜索,首先要确定递归边界:1. 超过矩阵边界要返回; 2. 已经被标记的要返回; 3. 和上一个连通点的颜色不一样的话要返回。
(5) 通过第(4)步就很好确定要递归哪些元素,也就是dfs函数里面有哪些参数需要传输。分别是矩阵本身,矩阵的长宽的值,点的起始位置的坐标,上一个点的颜色。
(6) 深搜完成之后,要改变连通的面积的边界的点的颜色为color。所以要遍历整个矩阵,当一个点的下一步是矩阵的边界,或者是没有在深搜的时候被标记的点,那么这个点就是边界。更新该点的值,并返回矩阵。
class Solution {
int hash[55][55];
int dir[4][2] = {
{0, 1},
{0, -1},
{1, 0},
{-1, 0}
};
void dfs(vector<vector<int>>& grid, int m, int n, int row, int col, int precolor) {
//递归边界
if(row < 0 || col < 0 || row == m || col == n || hash[row][col] == 1) {
return;
}
if(grid[row][col] != precolor) {
return;
}
//标记
hash[row][col] = 1;
for(int k = 0; k < 4; ++k) {
int xk = row + dir[k][0];
int yk = col + dir[k][1];
dfs(grid, m, n, xk, yk, grid[row][col]);
}
}
public:
vector<vector<int>> colorBorder(vector<vector<int>>& grid, int row, int col, int color) {
int m = grid.size();
int n = grid[0].size();
memset(hash, 0, sizeof(hash));
dfs(grid, m, n, row, col, grid[row][col]);
vector<vector<int>> temp(grid);
int i, j, k;
for(i = 0; i < m; ++i) {
for(j = 0; j < n; ++j) {
//如果没有被标记,就直接跳过
if(hash[i][j] == 0) continue;
temp[i][j] = grid[i][j];
//如果下一步会出界,就说明是边界,就改变color
for(k = 0; k < 4; ++k) {
int xi = i + dir[k][0];
int yj = j + dir[k][1];
if(xi < 0 || yj < 0||xi == m || yj == n || hash[xi][yj] == 0) {
temp[i][j] = color;
}
}
}
}
return temp;
}
};