题目来自LeetCode,链接:生命游戏。具体描述为:生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 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]
]
简单地可以用一个额外的 m × n m\times n m×n数组保存输入数组,然后再修改原数组,这样当然会额外占用空间,所以空间复杂度为 O ( m n ) O(mn) O(mn),时间复杂度则为 O ( m n ) O(mn) O(mn)。
JAVA版代码如下:
class Solution {
private int[][] copy;
private int count(int i, int j) {
int num = 0;
for (int m = Math.max(0, i - 1); m <= Math.min(copy.length - 1, i + 1); ++m) {
for (int n = Math.max(0, j - 1); n <= Math.min(copy[0].length - 1, j + 1); ++n) {
if (copy[m][n] == 1) {
++num;
}
}
}
return num - copy[i][j];
}
public void gameOfLife(int[][] board) {
int row = board.length, col = board[0].length;
copy = new int[row][col];
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
copy[i][j] = board[i][j];
}
}
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
int onesCount = count(i, j);
if (copy[i][j] == 0 && onesCount == 3) {
board[i][j] = 1;
}
else if (copy[i][j] == 1 && (onesCount < 2 || onesCount > 3)) {
board[i][j] = 0;
}
}
}
}
}
提交结果如下:
接下来优化空间复杂度,为了避免原地操作的时候影响在此之后的判断,我们需要记录操作:比如用2代表将1改为0,用-1表示将0改为1,这样后面遇到这两个非0/1的数字的时候就可以知道原先他们都是什么数来着。这样的时间复杂度为 O ( m n ) O(mn) O(mn),空间复杂度为 O ( 1 ) O(1) O(1)。
JAVA版代码如下:
class Solution {
private final int zero2one = -1;
private final int one2zero = 2;
private int count(int[][] board, int i, int j) {
int num = 0;
for (int m = Math.max(0, i - 1); m <= Math.min(board.length - 1, i + 1); ++m) {
for (int n = Math.max(0, j - 1); n <= Math.min(board[0].length - 1, j + 1); ++n) {
if (board[m][n] >= 1) {
++num;
}
}
}
return num - board[i][j];
}
public void gameOfLife(int[][] board) {
int row = board.length, col = board[0].length;
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
int onesCount = count(board, i, j);
if (board[i][j] == 0 && onesCount == 3) {
board[i][j] = zero2one;
}
else if (board[i][j] == 1 && (onesCount < 2 || onesCount > 3)) {
board[i][j] = one2zero;
}
}
}
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
if (board[i][j] == one2zero) {
board[i][j] = 0;
}
else if (board[i][j] == zero2one) {
board[i][j] = 1;
}
}
}
}
}
提交结果如下:
Python版代码如下:
class Solution:
def gameOfLife(self, board: List[List[int]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
one2zero = 2
zero2one = -1
row = len(board)
col = len(board[0])
def count(i, j):
num = 0
for m in range(max(0, i - 1), min(i + 2, row)):
for n in range(max(0, j - 1), min(j + 2, col)):
if board[m][n] >= 1:
num += 1
return num - board[i][j]
for i in range(row):
for j in range(col):
onesNum = count(i, j)
if board[i][j] == 0 and onesNum == 3:
board[i][j] = zero2one
elif board[i][j] == 1 and (onesNum < 2 or onesNum >3):
board[i][j] = one2zero
for i in range(row):
for j in range(col):
if board[i][j] == one2zero:
board[i][j] = 0
elif board[i][j] == zero2one:
board[i][j] = 1
提交结果如下: