谷歌历年面试真题——数组和字符串系列真题练习。
生命游戏
根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
- 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
- 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
- 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
- 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。
示例 1:
输入:board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
示例 2:
输入:board = [[1,1],[1,0]]
输出:[[1,1],[1,1]]
提示:
- m == board.length
- n == board[i].length
- 1 <= m, n <= 25
- board[i][j] 为 0 或 1
思路一:模拟细胞状态转换
- 遍历整个网格面板,对于每个细胞,计算其周围的活细胞数量。
- 根据题目给出的生存定律,更新每个细胞的状态:
- 如果一个活细胞周围的活细胞数量少于两个或多于三个,则该细胞死亡。
- 如果一个死细胞周围有三个活细胞,则该细胞复活。
- 其他情况下,细胞的状态不变。
- 将更新后的网格返回作为下一个状态。
下面是相应的 Python 代码实现:
def gameOfLife(board):
m, n = len(board), len(board[0])
directions = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]
# 获取周围活细胞数量
def countLiveNeighbors(row, col):
count = 0
for dx, dy in directions:
x, y = row + dx, col + dy
if 0 <= x < m and 0 <= y < n and (board[x][y] == 1 or board[x][y] == -1):
count += 1
return count
# 更新细胞状态
for i in range(m):
for j in range(n):
live_neighbors = countLiveNeighbors(i, j)
if board[i][j] == 1 and (live_neighbors < 2 or live_neighbors > 3):
board[i][j] = -1 # 表示活细胞死亡
if board[i][j] == 0 and live_neighbors == 3:
board[i][j] = 2 # 表示死细胞复活
# 更新细胞状态
for i in range(m):
for j in range(n):
if board[i][j] == -1:
board[i][j] = 0
elif board[i][j] == 2:
board[i][j] = 1
return board
# 示例 1
board1 = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
print(gameOfLife(board1)) # 输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
# 示例 2
board2 = [[1,1],[1,0]]
print(gameOfLife(board2)) # 输出:[[1,1],[1,1]]
这个函数实现了生命游戏的状态转换逻辑,并返回下一个状态的网格面板。
思路二:使用额外标记实现状态转换
除了模拟细胞状态转换的方法外,还可以通过使用额外的标记来实现状态转换,而不对原始网格进行修改。具体思路如下:
- 遍历整个网格面板,对于每个细胞,计算其周围的活细胞数量。
- 根据题目给出的生存定律,不修改原始网格,而是使用额外的标记表示下一个状态:
- 如果一个活细胞周围的活细胞数量少于两个或多于三个,则该细胞在下一个状态为死亡,标记为 -1。
- 如果一个死细胞周围有三个活细胞,则该细胞在下一个状态为活细胞,标记为 2。
- 其他情况下,细胞的状态不变,标记为原始状态。
- 根据标记更新网格,返回下一个状态的网格。
下面是相应的 Python 代码实现:
def gameOfLife(board):
m, n = len(board), len(board[0])
directions = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]
# 获取周围活细胞数量
def countLiveNeighbors(row, col):
count = 0
for dx, dy in directions:
x, y = row + dx, col + dy
if 0 <= x < m and 0 <= y < n and (board[x][y] == 1 or board[x][y] == -1):
count += 1
return count
# 根据生存定律标记下一个状态
for i in range(m):
for j in range(n):
live_neighbors = countLiveNeighbors(i, j)
if board[i][j] == 1 and (live_neighbors < 2 or live_neighbors > 3):
board[i][j] = -1 # 表示活细胞死亡
if board[i][j] == 0 and live_neighbors == 3:
board[i][j] = 2 # 表示死细胞复活
# 更新网格状态
for i in range(m):
for j in range(n):
if board[i][j] == -1:
board[i][j] = 0
elif board[i][j] == 2:
board[i][j] = 1
return board
# 示例 1
board1 = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
print(gameOfLife(board1)) # 输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
# 示例 2
board2 = [[1,1],[1,0]]
print(gameOfLife(board2)) # 输出:[[1,1],[1,1]]
这个解法避免了直接修改原始网格,而是通过标记的方式表示下一个状态,然后再根据标记更新网格。
思路三:状态压缩
除了模拟细胞状态转换和使用额外标记的方法外,还可以使用状态压缩的方法解决这个问题。状态压缩是一种优化技巧,用于减少空间复杂度。
- 遍历整个网格面板,对于每个细胞,计算其周围的活细胞数量。
- 根据题目给出的生存定律,根据当前细胞的状态和周围的活细胞数量,计算出下一个状态的细胞状态。
- 使用额外的状态表示方法,将下一个状态的细胞状态编码到一个二进制数中,这样就可以使用一个二进制数来表示整个网格面板的下一个状态。
- 根据编码后的状态生成新的网格面板。
下面是相应的 Python 代码实现:
def gameOfLife(board):
m, n = len(board), len(board[0])
directions = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]
# 获取周围活细胞数量
def countLiveNeighbors(row, col):
count = 0
for dx, dy in directions:
x, y = row + dx, col + dy
if 0 <= x < m and 0 <= y < n and (board[x][y] == 1 or board[x][y] == -1):
count += 1
return count
# 计算下一个状态的细胞状态并编码
for i in range(m):
for j in range(n):
live_neighbors = countLiveNeighbors(i, j)
if board[i][j] == 1 and (live_neighbors < 2 or live_neighbors > 3):
board[i][j] = 2 # 表示当前细胞死亡,下一个状态仍为死亡
if board[i][j] == 0 and live_neighbors == 3:
board[i][j] = -1 # 表示当前细胞复活,下一个状态为活细胞
# 解码并更新网格状态
for i in range(m):
for j in range(n):
if board[i][j] == 2:
board[i][j] = 0
elif board[i][j] == -1:
board[i][j] = 1
return board
# 示例 1
board1 = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
print(gameOfLife(board1)) # 输出:[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
# 示例 2
board2 = [[1,1],[1,0]]
print(gameOfLife(board2)) # 输出:[[1,1],[1,1]]
这个解法通过状态压缩的方式,用一个二进制数表示整个网格面板的下一个状态,从而减少了空间复杂度。