图解题库 / 矩阵模拟

289. 生命游戏

中等 含交互动画

学完这道你应该能: 说清它为什么用「矩阵模拟」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

每个细胞 活(1)/死(0)。下一代:活细胞周围2~3 个活则存活,否则死;死细胞周围恰好 3 个活则复活。所有细胞同时更新。

board = 竖条 010/010/010
输出 = 横条 000/111/000

思路解析

用 4 个数编码:0 死、1 活、2 将生(原死下步活)、3 将死(原活下步死)。数邻居时把 1 和 3 都算「原来是活的」。全部标记完,再统一把 2→1、3→0。

初始 · 竖条:绿色是活细胞,组成一根竖条。来逐个判断每个细胞的命运。

判断一个细胞的命运,要数它上下左右及四个斜角共 8 个邻居里有几个是活的,再套规则。数的时候用「旧状态」。

看中心 (1,1):中心 (1,1) 是活的,上下两个活邻居 = 2 个,符合「2~3 存活」→ 它继续活,保持 1。

看 (0,1):(0,1) 是活的,但只有 1 个活邻居(下方),少于 2 → 下一步死。标成 将死(3),但本轮数邻居时它仍算「活」。

看 (1,0):(1,0) 是死的,但周围 (0,1)、(1,1)、(2,1) 恰好 3 个活 → 复活!标成 将生(2)

同理 (1,2)、(2,1):(1,2) 同样 3 个活邻居 → 将生;(2,1) 和 (0,1) 一样只有 1 个活邻居 → 将死。全部标记完毕。

统一还原:最后扫一遍:将生(2)→活(1)、将死(3)→死(0)。竖条变成了横条。这就是「同时更新」的效果。

需要「同时」更新又想原地的问题,常用「编码中间状态、最后统一还原」这一招。

参考代码(Python)

Python
1for i in range(m):
2    for j in range(n):
3        live = 数周围 8 个里值为 13 的个数   # 3 也算原来活的
4        if board[i][j]==1 and (live<2 or live>3): board[i][j]=3  # 活→将死
5        if board[i][j]==0 and live==3: board[i][j]=2            # 死→将生
6for i,j: board[i][j] = 1 if board[i][j]==2 else (0 if board[i][j]==3 else board[i][j])
7        # 等价: 将生2→1, 将死3→0

复杂度分析

  • 时间复杂度:O(m×n) —— 每格看 8 邻居,常数
  • 空间复杂度:O(1) —— 原地,用中间状态

套路模板

记住骨架:两遍扫描——先编码同时记"旧+新"、数邻居时 1 和 3 都算"原来活",再统一还原。矩阵置零的"特殊标记法"也是同一招。

Python
1# 编码: 0死 1活 2将生(死→活) 3将死(活→死)
2for i, j in 所有格子:
3    live = sum(board[x][y] in (1,3) for x,y in 8邻居)  # 13都算class="cl-str">"原来活"!
4    if board[i][j]==1 and not 2<=live<=3: board[i][j]=3  # 活→将死
5    elif board[i][j]==0 and live==3: board[i][j]=2       # 死→将生
6for i, j in 所有格子:
7    board[i][j] = {2:1, 3:0}.get(board[i][j], board[i][j])  # 统一还原

易错点

  • 数邻居只数值为 1 的 → ✅ 把 3(将死) 也算「原来是活的」(它本轮还没真正死,邻居计数要用旧状态)
  • 边遍历边改成 0/1 → ✅ 用 2/3 中间态,最后统一还原(直接改会影响后面格子的邻居计数)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →743. 网络延迟时间 ← 返回题库