矩阵
矩阵题目核心在于坐标变换、分层(圈)扫描与空间映射。
36. 有效的数独 (Valid Sudoku)
题目描述
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
- 数字
1-9在每一行只能出现一次。 - 数字
1-9在每一列只能出现一次。 - 数字
1-9在每一个以粗实线分隔的3x3宫内只能出现一次。(请参考下图)
注意:
- 一个有效的数独(部分填满)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 空白格用
'.'表示。
解题思路
一次遍历 + 三个标记数组。 用三个二维数组分别记录:
row[9][9]:记录第i行数字j+1是否出现过。col[9][9]:记录第i列数字j+1是否出现过。box[3][3][9]:记录第(i/3, j/3)个九宫格中数字j+1是否出现过。
Java 代码实现
class Solution {
public boolean isValidSudoku(char[][] board) {
int[][] rows = new int[9][9], cols = new int[9][9], boxes = new int[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] != '.') {
int num = board[i][j] - '1';
int boxIndex = (i / 3) * 3 + j / 3;
if (rows[i][num] == 1 || cols[j][num] == 1 || boxes[boxIndex][num] == 1) return false;
rows[i][num] = cols[j][num] = boxes[boxIndex][num] = 1;
}
}
}
return true;
}
}
54. 螺旋矩阵 (Spiral Matrix)
题目描述
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
解题思路
分层收缩边界。
- 设置
top, bottom, left, right四个指针分别指向矩阵上下左右边界。 - 按照
左->右,上->下,右->左,下->上的顺序遍历并收缩对应的边界。 - 注意在左右、下上遍历前判断边界是否已经重合,防止重复。
Java 代码实现
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
if (matrix.length == 0) return res;
int t = 0, b = matrix.length - 1, l = 0, r = matrix[0].length - 1;
while (true) {
for (int i = l; i <= r; i++) res.add(matrix[t][i]);
if (++t > b) break;
for (int i = t; i <= b; i++) res.add(matrix[i][r]);
if (--r < l) break;
for (int i = r; i >= l; i--) res.add(matrix[b][i]);
if (--b < t) break;
for (int i = b; i >= t; i--) res.add(matrix[i][l]);
if (++l > r) break;
}
return res;
}
}
48. 旋转图像 (Rotate Image)
题目描述
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
解题思路
转置 + 镜像反转。
- 观察发现顺时针 90 度旋转等价于:先沿主对角线
(i, j) <-> (j, i)进行翻转(转置)。 - 然后对每一行进行左右翻转(镜像)。
Java 代码实现
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
// 转置
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
int tmp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = tmp;
}
}
// 镜像
for (int i = 0; i < n; i++) {
for (int j = 0; j < n / 2; j++) {
int tmp = matrix[i][j];
matrix[i][j] = matrix[i][n - 1 - j];
matrix[i][n - 1 - j] = tmp;
}
}
}
}
73. 矩阵置零 (Set Matrix Zeroes)
题目描述
给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
解题思路
首行首列标记位。
- 使用矩阵的第一行和第一列来存储对应的列和行是否需要被置零。
- 首先用两个布尔变量记录第一行和第一列原本是否包含 0。
- 遍历其余部分(从
1, 1开始),如果有matrix[i][j] == 0,标记matrix[i][0] = 0和matrix[0][j] = 0。 - 根据标记位更新内部其余元素。
- 最后根据步骤2的变量更新第一行和第一列。
Java 代码实现
class Solution {
public void setZeroes(int[][] matrix) {
boolean row0 = false, col0 = false;
int m = matrix.length, n = matrix[0].length;
for (int i = 0; i < m; i++) if (matrix[i][0] == 0) col0 = true;
for (int j = 0; j < n; j++) if (matrix[0][j] == 0) row0 = true;
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][j] == 0) matrix[i][0] = matrix[0][j] = 0;
}
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
if (matrix[i][0] == 0 || matrix[0][j] == 0) matrix[i][j] = 0;
}
}
if (row0) for (int j = 0; j < n; j++) matrix[0][j] = 0;
if (col0) for (int i = 0; i < m; i++) matrix[i][0] = 0;
}
}
289. 生命游戏 (Game of Life)
题目描述
根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
- 如果活细胞周围八个相邻位置的活细胞数少于两个,则该细胞死亡。
- 如果活细胞周围八个相邻位置有两个或三个活细胞,则该细胞保持存活。
- 如果活细胞周围八个相邻位置有超过三个活细胞,则该细胞死亡。
- 如果死细胞周围正好有三个活细胞,则该细胞变成活细胞。
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞生成的,其中生死的替换同时发生。你需要将面板从当前状态更新为下一个状态。
注意: 你必须 原地 修改面板。
解题思路
复合状态位标记法。 可以使用状态机逻辑记录四个状态转移:
- 状态 0:死细胞。
- 状态 1:活细胞。
- 状态 2:活 -> 死。
- 状态 -1:死 -> 活。
这样在统计邻居时,只要
abs(cell) == 1 || cell == 2都说明之前是活的。 遍历结束后,将 2 变为 0,-1 变为 1。
Java 代码实现
class Solution {
public void gameOfLife(int[][] board) {
int[] dx = {-1, -1, -1, 0, 0, 1, 1, 1}, dy = {-1, 0, 1, -1, 1, -1, 0, 1};
int m = board.length, n = board[0].length;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int cnt = 0;
for (int k = 0; k < 8; k++) {
int nx = i + dx[k], ny = j + dy[k];
if (nx >= 0 && nx < m && ny >= 0 && ny < n && (board[nx][ny] == 1 || board[nx][ny] == 2)) cnt++;
}
if (board[i][j] == 1 && (cnt < 2 || cnt > 3)) board[i][j] = 2;
if (board[i][j] == 0 && cnt == 3) board[i][j] = -1;
}
}
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 2) board[i][j] = 0;
if (board[i][j] == -1) board[i][j] = 1;
}
}
}
}