一、题目描述
根据百度百科,生命游戏(Game of Life)是英国数学家 约翰·何顿·康威 在 1970 年提出的细胞自动机模型。
给定一个 m × n 的网格 board,每个位置表示一个细胞:
-
1表示 活细胞 -
0表示 死细胞
每个细胞与其 八个相邻位置(上下左右 + 四个对角线)共同决定下一状态。
细胞演化规则
1️⃣ 活细胞周围 少于 2 个活细胞 → 死亡(模拟人口不足)
2️⃣ 活细胞周围 2 或 3 个活细胞 → 继续存活
3️⃣ 活细胞周围 超过 3 个活细胞 → 死亡(模拟人口过多)
4️⃣ 死细胞周围 恰好 3 个活细胞 → 复活
要求:
所有细胞必须同时更新
二、解题思路
如果直接修改 board[i][j],会影响后续邻居统计。
例如:
先更新 (0,0)
再计算 (0,1)
此时 (0,1) 读取到的是 更新后的状态,结果就错了。
解决办法:状态标记法(原地算法)
利用额外状态记录:
| 原状态 | 新状态 | 编码 |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 1 | 0 | -1 |
| 0 | 1 | 2 |
解释:
-
-1:原来是 活细胞 → 死亡 -
2:原来是 死细胞 → 复活
判断邻居是否原本是活细胞:
abs(board[x][y]) == 1
因为:
1 -> 原本活
-1 -> 原本活
三、算法流程
第一步:统计邻居
遍历每个细胞 (i,j),统计 8 个方向活细胞数量。
方向数组:
(-1,-1) (-1,0) (-1,1)
(0,-1) (0,1)
(1,-1) (1,0) (1,1)
第二步:更新状态
如果当前是活细胞:
live < 2 或 live > 3
→ 标记为 -1
如果当前是死细胞:
live == 3
→ 标记为 2
第三步:恢复最终状态
最后再遍历一遍:
>0 → 1
<=0 → 0
四、C语言实现
#include <stdlib.h>
void gameOfLife(int** board, int boardSize, int* boardColSize) {
int m = boardSize;
int n = boardColSize[0];
int dirs[8][2] = {
{-1,-1},{-1,0},{-1,1},
{0,-1}, {0,1},
{1,-1},{1,0},{1,1}
};
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
int live = 0;
for(int k = 0; k < 8; k++)
{
int x = i + dirs[k][0];
int y = j + dirs[k][1];
if(x >= 0 && x < m && y >= 0 && y < n && abs(board[x][y]) == 1)
live++;
}
if(board[i][j] == 1)
{
if(live < 2 || live > 3)
board[i][j] = -1;
}
else
{
if(live == 3)
board[i][j] = 2;
}
}
}
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
if(board[i][j] > 0)
board[i][j] = 1;
else
board[i][j] = 0;
}
}
}
五、复杂度分析
时间复杂度
O(m × n)
每个细胞最多检查 8 个邻居。
空间复杂度
O(1)
使用 状态标记法实现原地更新,没有额外数组。
六、总结
本题核心思想:
使用额外状态记录"过去状态"
关键技巧:
1 -> 0 用 -1
0 -> 1 用 2
判断原状态:
abs(board[i][j]) == 1
这样就能保证:
在更新过程中仍然可以获取到 原始状态
从而实现 原地更新矩阵。