一、题目描述
根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n
个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1
即为 活细胞 (live),或 0
即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
- 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
- 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
- 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
- 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是 同时 发生的。给你 m x n
网格面板 board
的当前状态,返回下一个状态。
给定当前 board
的状态,更新 board
到下一个状态。
注意 你不需要返回任何东西。
示例 1:
输入:board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]] 输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
示例 2:
输入:board = [[1,1],[1,0]]
输出:[[1,1],[1,1]]
提示:
m == board.length
n == board[i].length
1 <= m, n <= 25
board[i][j] 为 0 或 1
进阶:
你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
二、题目分析
这道题的进阶难点就是一次性同时更新所有格子状态,不然一个一个更新就会影响后续的状态判断,所以更新的操作放在最后一步操作,只要在过程中记录需要更新的信息,就可以实现一口气更新完,进而不会影响后续的状态判断。这道题的思路没有什么难的点,主要就是规则上的理解:
1. 原来是活的,周围有2-3个活的,成为活的
2. 原来是死的,周围有3个活的,成为活的
3. 其他都是死了
遍历的时候,如果这个格子里是活的,就让它去“影响”周围的八个格子。这样一来,大批原来是死了的格子就省了很多检查的时间。给被影响的格子里的数字加10。这样一来,个位存着这格子原来的状态,十位就存着它周围有多少个活格子了。比如遍历完了之后一个格子里是41,那就表示它原来自己是1,然后被周围的四个活格子加了四个10,于是周围有四个活细胞。等之后再遍历一遍,更新到最新状态就ok了。
三、代码实现
代码说明:
gameOfLife
方法:- 这是主方法,负责遍历整个棋盘,调用
affect
和calculate
方法来更新每个细胞的状态。
- 这是主方法,负责遍历整个棋盘,调用
affect
方法:- 这个方法用于影响当前细胞的周围细胞。如果当前细胞是活的(值为 1),则将其周围细胞的数值增加 10。
calculate
方法:- 这个方法用于计算每个细胞的下一代状态。根据周围活细胞的数量(通过
value / 10
计算)和当前细胞的状态(通过value % 10
计算),决定下一代细胞是活的还是死的。
- 这个方法用于计算每个细胞的下一代状态。根据周围活细胞的数量(通过
class Solution {
public void gameOfLife(int[][] board) {
int m = board.length;
int n = board[0].length;
// 遍历每个细胞,影响其周围的细胞
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] % 10 == 1) {
affect(board, i, j, m, n);
}
}
}
// 计算每个细胞的下一代状态
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
calculate(board, i, j);
}
}
}
private void affect(int[][] board, int x, int y, int m, int n) {
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if (i < 0 || i >= m || j < 0 || j >= n || (i == x && j == y)) {
continue;
}
board[i][j] += 10;
}
}
}
private void calculate(int[][] board, int x, int y) {
int value = board[x][y];
if (value / 10 == 3) {
board[x][y] = 1;
} else if (value / 10 == 2 && value % 10 == 1) {
board[x][y] = 1;
} else {
board[x][y] = 0;
}
}
}
提交结果
四、总结
这类设计矩阵类的问题,主要就是考虑边界情况和更新时的状态变化,只要按照规则一次操作九宫格内的数据,有耐心写完规则基本上就不会有太大问题,矩阵类的时间复杂度基本都不会低,所以更多的操作空间优化都在空间复杂度上面,毕竟一次就是mn的空间消耗,还是需要多做题目来提高自己的熟练度。