289-Game-of-Life
0x0 题目详情
根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律: 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡; 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活; 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡; 如果死细胞周围正好有三个活细胞,则该位置死细胞复活; 根据当前状态,写一个函数来计算面板上所有细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
测试用例:
示例: 输入: [ [0,1,0], [0,0,1], [1,1,1], [0,0,0] ] 输出: [ [0,0,0], [1,0,1], [0,1,1], [0,1,0] ]
进阶: 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
0x1 解题思路
关于矩阵的题目,一般会有两个小技巧:
如果要对矩阵中的数据全部同步更新,那么一般需要两次遍历完成,第一次遍历设置标记,即哪些数据需要改变,变成多少,第二遍根据标记设置新的值
如果要探索矩阵中一个位置的周边元素,一个一个通过计算下标太麻烦了,有两种办法:
在矩阵四周包一层没有用的数据,这样就不会产生下标越界问题
设置两个偏移数组,数组中的内容为一个位置距周围下标的偏移,每遇到一个新位置,就利用偏移数组获取周围所有元素的下标,减少了每次手动下标的麻烦,也不容易出错。
这道题的意思时:
如果一个位置上的元素为1,且周围位置的1的个数小于2个或大于3个,那么就把当前位置的元素设0
如果一个位置上的元素为0,且周围位置1的个数为3个,那么就把当前位置的元素设为1
我们可以是使用两个标志位来标记这种转换;
-1表示虽然本轮是活的,但是下一轮就要死了
2表示虽然本轮是死的,但是下一轮就活了
具体的代码见下:
0x2 代码实现
时间复杂度为O(M*N),空间复杂度为O(M)
class Solution {
public void gameOfLife(int[][] board) {
if(board==null || board.length==0){
return;
}
int[] row={-1,-1,-1,0,0,1,1,1};
int[] col={-1,0,1,-1,1,-1,0,1};
//第一轮遍历,给元素打标记
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
int curRow=0;
int curCol=0;
int live=0;
for(int k=0;k<8;k++){
curRow=i+row[k];
curCol=j+col[k];
if(curRow<0|| curRow>=board.length|| curCol<0 || curCol>=board[0].length){
continue;
}
//-1表示虽然现在是活的,但是下一轮就要死了,所以计算周围活细胞时,-1也要被算入
if(board[curRow][curCol]==1||board[curRow][curCol]==-1){
live++;
}
}
//给死细胞打标记
if(board[i][j]==0 && live==3){
board[i][j]=2;
//给活细胞打标记
}else if(board[i][j]==1 && (live<2|| live>3)){
board[i][j]=-1;
}
}
}
//第二轮遍历,设置新的值
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
if(board[i][j]==-1){
board[i][j]=0;
}
if(board[i][j]==2){
board[i][j]=1;
}
}
}
return;
}
}
0x3 课后总结
矩阵题目小技巧:
一个位置元素的改变会影响到周边元素,那么一般需要两遍遍历,给元素先打标记,再统一变化
获取一个位置的周边元素时,可以设置偏移数组或者包一层无用数据。
Last updated
Was this helpful?