题目描述
根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
- 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
- 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
- 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
- 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
根据当前状态,写一个函数来计算面板上所有细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
进阶:
- 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
- 本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
思路分析
辅助数组
因为面板上所有的格子都是一个整体,要变就一起变。所以用一个相同大小的数组存储更新后的值,最后将值赋给原数组。
具体的逻辑题目中描述得很清楚,看下面具体代码也行。
原地解法-two bits状态机
评论区学到的。
- 利用一个 two bits 的状态机来记录细胞状态, 第一位表示下一状态, 第二位表示当前状态:
- 00: dead (next state) <- dead (current state)
- 01: dead (next state) <- live (current state)
- 10: live (next state) <- dead (current state)
- 11: live (next state) <- live (current state) - 初始情况对应就是 00 和 01 (默认下一状态是 dead state)
- 统计每个位置周围的 live 细胞数决定高位置 1 (live)还是 0 (dead)
- 最后右移一位即为最终状态, 注意不需要考虑 01 以及 00 的情况, 因为已经默认下一状态为 dead.
生命游戏的模拟,可以去玩一玩这个:生命游戏
代码实现
/**
* 辅助数组解法
* @param board
*/
public static void gameOfLife(int[][] board) {
//用一个数组存储改变后的数组数组
if (board == null || board.length == 0 || board[0].length == 0) {
return;
}
int row = board.length, col = board[0].length;
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
matrix[i][j] = changeStatus(board, row, col, i, j, board[i][j] == 1 ? true : false);
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = matrix[i][j];
}
}
}
static int[] dx = {-1, -1, -1, 0, 0, 1, 1, 1};
static int[] dy = {-1, 0, 1, -1, 1, -1, 0, 1};
public static int changeStatus(int[][] board, int row, int col, int i, int j, boolean alive) {
int sum = 0;
for (int k = 0; k < 8; k++) {
sum += count(board, row, col, dx[k] + i, dy[k] + j);
}
//原来为活细胞
if (alive && (sum < 2 || sum > 3)) {
return 0;
} else if (alive && (sum == 2 || sum == 3)) {
return 1;
} else if (!alive && sum == 3) {
//原来为死细胞
return 1;
}
return 0;
}
public static int count(int[][] board, int row, int col, int i, int j) {
if (i < 0 || i >= row || j < 0 || j >= col) {
return 0;
}
return board[i][j];
}
/**
* 原地解法
* 用 2比特 的状态机记录细胞状态
* 第一位表示下一状态,第二位表示当前状态
*
* @param board
*/
public static void gameOfLife2(int[][] board) {
//用一个数组存储改变后的数组数组
if (board == null || board.length == 0 || board[0].length == 0) {
return;
}
int row = board.length, col = board[0].length;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
int count = 0;
for (int k = 0; k < 8; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x < 0 || x >= row || y < 0 || y >= col) {
continue;
}
count += (board[x][y] & 1);
}
//活 -> 活
if ((board[i][j] & 1) == 1) {
if (count >= 2 || count <= 3) {
board[i][j] = 0b11;//3
}
//死 -> 活
} else if (count == 3) {
board[i][j] = 0b10;//2
}
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] >>= 1;
}
}
}