[LeetCode] 289. Game of Life 生命游戏
According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970."
Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):
- Any live cell with fewer than two live neighbors dies, as if caused by under-population.
- Any live cell with two or three live neighbors lives on to the next generation.
- Any live cell with more than three live neighbors dies, as if by over-population..
- Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
Write a function to compute the next state (after one update) of the board given its current state.
Follow up:
- Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells.
- In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems?
Credits:
Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases.
康威生命游戏(英语:Conway's Game of Life),又称康威生命棋,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。
生命游戏中,对于任意细胞,规则如下:
每个细胞有两种状态-存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。
- 当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
- 当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
- 当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
- 当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)
可以把最初的细胞结构定义为种子,当所有在种子中的细胞同时被以上规则处理后, 可以得到第一代细胞图。按规则继续处理当前的细胞图,可以得到下一代的细胞图,周而复始。
解法1:最简单的方法是新建一个矩阵保存新结果,但题目要求in-place解时,如果直接根据每个点周围的存活数量来修改当前值,由于矩阵是顺序遍历的,这样会影响到下一个点的计算。如何在修改值的同时又保证下一个点的计算不会被影响呢?实际上我们只要将值稍作编码就行了,因为题目给出的是一个int矩阵,大有空间可以利用。这里我们假设对于某个点,值的含义为
0 : 上一轮是0,这一轮过后还是0
1 : 上一轮是1,这一轮过后还是1
2 : 上一轮是1,这一轮过后变为0
3 : 上一轮是0,这一轮过后变为1
这样,对于一个节点来说,如果它周边的点是1或者2,就说明那个点上一轮是活的。最后,在遍历一遍数组,把我们编码再解回去,即0和2都变回0,1和3都变回1,就行了。
解法2: 位运算(bit manipulation),由于细胞只有两种状态0和1,因此可以使用二进制来表示细胞的生存状态,更新细胞状态时,将细胞的下一个状态用高位进行存储,全部更新完毕后,将细胞的状态右移一位。
参考:ethannnli
Java:
public class Solution { public void gameOfLife(int[][] board) { int m = board.length, n = board[0].length; for(int i = 0; i < m; i++){ for(int j = 0; j < n; j++){ int lives = 0; // 判断上边 if(i > 0){ lives += board[i - 1][j] == 1 || board[i - 1][j] == 2 ? 1 : 0; } // 判断左边 if(j > 0){ lives += board[i][j - 1] == 1 || board[i][j - 1] == 2 ? 1 : 0; } // 判断下边 if(i < m - 1){ lives += board[i + 1][j] == 1 || board[i + 1][j] == 2 ? 1 : 0; } // 判断右边 if(j < n - 1){ lives += board[i][j + 1] == 1 || board[i][j + 1] == 2 ? 1 : 0; } // 判断左上角 if(i > 0 && j > 0){ lives += board[i - 1][j - 1] == 1 || board[i - 1][j - 1] == 2 ? 1 : 0; } //判断右下角 if(i < m - 1 && j < n - 1){ lives += board[i + 1][j + 1] == 1 || board[i + 1][j + 1] == 2 ? 1 : 0; } // 判断右上角 if(i > 0 && j < n - 1){ lives += board[i - 1][j + 1] == 1 || board[i - 1][j + 1] == 2 ? 1 : 0; } // 判断左下角 if(i < m - 1 && j > 0){ lives += board[i + 1][j - 1] == 1 || board[i + 1][j - 1] == 2 ? 1 : 0; } // 根据周边存活数量更新当前点,结果是0和1的情况不用更新 if(board[i][j] == 0 && lives == 3){ board[i][j] = 3; } else if(board[i][j] == 1){ if(lives < 2 || lives > 3) board[i][j] = 2; } } } // 解码 for(int i = 0; i < m; i++){ for(int j = 0; j < n; j++){ board[i][j] = board[i][j] % 2; } } } }
Java: Bit Manipulate, 位操作,将下轮该cell要变的值存入bit2中,然后还原的时候右移就行了。
public void solveInplaceBit(int[][] board){ int m = board.length, n = board[0].length; for(int i = 0; i < m; i++){ for(int j = 0; j < n; j++){ int lives = 0; // 累加上下左右及四个角还有自身的值 for(int y = Math.max(i - 1, 0); y <= Math.min(i + 1, m - 1); y++){ for(int x = Math.max(j - 1, 0); x <= Math.min(j + 1, n - 1); x++){ // 累加bit1的值 lives += board[y][x] & 1; } } // 如果自己是活的,周边有两个活的,lives是3 // 如果自己是死的,周边有三个活的,lives是3 // 如果自己是活的,周边有三个活的,lives减自己是3 if(lives == 3 || lives - board[i][j] == 3){ board[i][j] |= 2; } } } // 右移就是新的值 for(int i = 0; i < m; i++){ for(int j = 0; j < n; j++){ board[i][j] >>>= 1; } } }
Python: Bit Manipulate
class Solution(object): def gameOfLife(self, board): """ :type board: List[List[int]] :rtype: void Do not return anything, modify board in-place instead. """ dx = (1, 1, 1, 0, 0, -1, -1, -1) dy = (1, 0, -1, 1, -1, 1, 0, -1) for x in range(len(board)): for y in range(len(board[0])): lives = 0 for z in range(8): nx, ny = x + dx[z], y + dy[z] lives += self.getCellStatus(board, nx, ny) if lives + board[x][y] == 3 or lives == 3: board[x][y] |= 2 for x in range(len(board)): for y in range(len(board[0])): board[x][y] >>= 1
def getCellStatus(self, board, x, y): if x < 0 or y < 0 or x >= len(board) or y >= len(board[0]): return 0 return board[x][y] & 1
Python:
class Solution(object): def gameOfLife(self, board): """ :type board: List[List[int]] :rtype: void Do not return anything, modify board in-place instead. """ m = len(board) n = len(board[0]) if m else 0 for i in xrange(m): for j in xrange(n): count = 0 ## Count live cells in 3x3 block. for I in xrange(max(i-1, 0), min(i+2, m)): for J in xrange(max(j-1, 0), min(j+2, n)): count += board[I][J] & 1 # if (count == 4 && board[i][j]) means: # Any live cell with three live neighbors lives. # if (count == 3) means: # Any live cell with two live neighbors. # Any dead cell with exactly three live neighbors lives. if (count == 4 and board[i][j]) or count == 3: board[i][j] |= 2 # Mark as live. for i in xrange(m): for j in xrange(n): board[i][j] >>= 1 # Update to the next state.
C++:
class Solution { public: void gameOfLife(vector<vector<int> >& board) { int m = board.size(), n = m ? board[0].size() : 0; int dx[] = {-1, -1, -1, 0, 1, 1, 1, 0}; int dy[] = {-1, 0, 1, 1, 1, 0, -1, -1}; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { int cnt = 0; for (int k = 0; k < 8; ++k) { int x = i + dx[k], y = j + dy[k]; if (x >= 0 && x < m && y >= 0 && y < n && (board[x][y] == 1 || board[x][y] == 2)) { ++cnt; } } if (board[i][j] && (cnt < 2 || cnt > 3)) board[i][j] = 2; else if (!board[i][j] && cnt == 3) board[i][j] = 3; } } for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { board[i][j] %= 2; } } } };