问题描述
根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 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]
]
进阶
- 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
- 本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
思路
设置好方向数组,遍历8个方向的状态。 由于细胞的出生和死亡是一起发生的,所以我们要设置一个bak数组来保存原本的状态。这样才不会影响到结果。(方法一)
进阶,如何原地修改呢? 因为本题用的是0,1代表了两种状态。int类型有32位bit,我们可以用其余的30个bit中的某一个来保存修改之后的状态。在这个例子中我们用倒数第二位来保存新状态,完事儿之后把这一位刷到最后一位即可。(方法二)
方法一
public void gameOfLife1(int[][] board) {
if(board.length == 0 || board[0].length == 0) return;
int[][] bak = new int[board.length][board[0].length];
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board[0].length; j++){
bak[i][j] = board[i][j];
}
}
int[] dictX = new int[]{-1,0,1,0,-1,1,-1,1};
int[] dictY = new int[]{0,-1,0,1,-1,1,1,-1};
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board[0].length; j++){
int count = 0;
for(int step = 0; step < 8; step++){
int tmpX = i + dictX[step], tmpY = j + dictY[step];
if(tmpX < 0 || tmpY < 0 || tmpX >= board.length || tmpY >= board[0].length){
continue;
}
if(bak[tmpX][tmpY] == 1){
count++;
}
}
if(bak[i][j] == 0 && count == 3) board[i][j] = 1;
else if(bak[i][j] == 1 && count < 2) board[i][j] = 0;
else if(bak[i][j] == 1 && (count == 2 || count == 3)){}
else if(bak[i][j] == 1 && count > 3) board[i][j] = 0;
}
}
}
方法二
public void gameOfLife(int[][] board) {
if(board.length == 0 || board[0].length == 0) return;
int[] direcX = new int[]{-1,0,1,0,-1,-1,1,1};
int[] direcY = new int[]{0,-1,0,1,-1,1,-1,1};
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board.length; j++){
int count = 0;
for(int step = 0; step < 8; step++){
int tmpX = i+direcX[step];
int tmpY = j+direcY[step];
if(tmpX<0 || tmpX >= board.length || tmpY<0 || tmpY>=board[0].length){
continue;
}
if((board[tmpX][tmpY]&1) == 1){
count++;
}
}
// 更新新状态
if((board[i][j]&1) == 0 && count == 3) board[i][j] |= 2;
else if((board[i][j]&1) == 1 && (count == 2 || count == 3)){
board[i][j] |= 2;
}
}
}
for(int i = 0; i < board.length; i++){
for(int j = 0; j < board[0].length; j++){
// 把新状态刷到最后一位
board[i][j] >>= 1;
}
}
}