关于二维数组(矩阵)的运算
参考力扣的官方题解来看:
根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 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]
]
进阶:
你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?
规则解读:
1.面板,即二维数组的操作,怎么去操作一个细胞对应的周围细胞,以及遇到边界怎么办?
2.知道了如何快速遍历一个细胞周围的细胞,就可以判断它们的状态,然后统计有几个活的。
3.知道了有几个活的就可以对应规则,决定这个中心的细胞的状态。
4.怎么实现同时,这里的同时不是说并行处理,而是所有的状态更新都是基于原面板。
解决:
1.二维数组:怎么访问到每一个细胞的周围8个细胞
计算二维数组的行和列
A
=
([1,2,5],[2,3,5],[3,4,5],[2,3,6])
print
len
(A)
# 4(直接计算数组名,得到数组的行)
print
len
(A[
0
])
# 3(计算数组的第1行的个数,得到数组的列)
使用numpy的计算方法
x = np.array([[1,2,5],[2,3,5],[3,4,5],[2,3,6]])
# 输出数组的行和列数
print x.shape # (4, 3)
# 只输出行数
print x.shape[0] # 4
# 只输出列数
print x.shape[1] # 3
得到数组的行列数才可以对数组进行两层for循环遍历。
怎么去操作一个细胞周围的8个细胞。如下所示:
将上面大矩阵中的元素下标加上左边下面的九宫格下标,就可以访问二维数组每个元素的周围8个坐标。当限定加了之后的横坐标值大于0且小于数组本身的行数,就可以在有效范围内访问。
neighbors = [(1,0), (1,-1), (0,-1), (-1,-1), (-1,0), (-1,1), (0,1), (1,1)]
rows = len(board)
cols = len(board[0])
# 遍历面板每一个格子里的细胞
for row in range(rows):
for col in range(cols):
# 访问每一个细胞及其八个相邻位置
for neighbor in neighbors:
r = (row + neighbor[0])
c = (col + neighbor[1])
# 限定边界
if (r < rows and r >= 0) and (c < cols and c >= 0)
在这个的基础上,添加判断访问到的细胞是否是活细胞,即元素值是否为1.
neighbors = [(1,0), (1,-1), (0,-1), (-1,-1), (-1,0), (-1,1), (0,1), (1,1)]
rows = len(board)
cols = len(board[0])
# 从原数组复制一份到 copy_board 中
copy_board = [[board[row][col] for col in range(cols)] for row in range(rows)]
# 遍历面板每一个格子里的细胞
for row in range(rows):
for col in range(cols):
# 对于每一个细胞统计其八个相邻位置里的活细胞数量
live_neighbors = 0
for neighbor in neighbors:
r = (row + neighbor[0])
c = (col + neighbor[1]) #这里需要定义新变量 r 和 c
# 查看相邻的细胞是否是活细胞
if (r < rows and r >= 0) and (c < cols and c >= 0) and (copy_board[r][c] == 1):
live_neighbors += 1
2.复制原面板:里面的copy_board是对原面板的复制,为了避免更新后的细胞影响后续细胞的判断,实现同时的功能。
copy_board = [[board[row][col] for col in range(cols)] for row in range(rows)]
这句相当于:
在求得面板的行数和列数之后,迭代将board的值复制到copy_board:
rows = len(board)
cols = len(board[0])
for row in range(rows):
for col in range(cols):
copy_board[row][col]=board[row][col]
使用上面的形式时注意写法:
1).copy_board直接为一个变量,没有带[]符号,想到了Python无处不对象的思想。
2).要把board[row][col]两边再用中括号括起来,[board[row][col],避免歧义
3).中间的中括号表示的是层的意思。即中括号从外往内读,最外一层中括号表示最外一层for循环,这里是for row in range(rows):
再到里面的中括号表示里面的for循环,for col in range(cols): ,再把最里面的值与最外面的赋值联系起来,copy_board=board[row][col]
3.根据规则判断细胞活性:后面的规则都是基于统计到的活细胞的数量来判断当前细胞的活性。根据原来细胞的活性以及统计到的周围细胞的活性,执行的是需要改变的情况。
答案:
class Solution:
def gameOfLife(self, board: List[List[int]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
neighbors = [(1,0), (1,-1), (0,-1), (-1,-1), (-1,0), (-1,1), (0,1), (1,1)]
rows = len(board)
cols = len(board[0])
# 从原数组复制一份到 copy_board 中
copy_board = [[board[row][col] for col in range(cols)] for row in range(rows)]
# 遍历面板每一个格子里的细胞
for row in range(rows):
for col in range(cols):
# 对于每一个细胞统计其八个相邻位置里的活细胞数量
live_neighbors = 0
for neighbor in neighbors:
r = (row + neighbor[0])
c = (col + neighbor[1])
# 查看相邻的细胞是否是活细胞
if (r < rows and r >= 0) and (c < cols and c >= 0) and (copy_board[r][c] == 1):
live_neighbors += 1
# 规则 1 或规则 3
if copy_board[row][col] == 1 and (live_neighbors < 2 or live_neighbors > 3):
board[row][col] = 0
# 规则 4
if copy_board[row][col] == 0 and live_neighbors == 3:
board[row][col] = 1