題目描述
根據 百度百科 ,生命遊戲,簡稱爲生命,是英國數學家約翰·何頓·康威在 1970 年發明的細胞自動機。
給定一個包含 m × n 個格子的面板,每一個格子都可以看成是一個細胞。每個細胞都具有一個初始狀態:1 即爲活細胞(live),或 0 即爲死細胞(dead)。每個細胞與其八個相鄰位置(水平,垂直,對角線)的細胞都遵循以下四條生存定律:
- 如果活細胞周圍八個位置的活細胞數少於兩個,則該位置活細胞死亡;
- 如果活細胞周圍八個位置有兩個或三個活細胞,則該位置活細胞仍然存活;
- 如果活細胞周圍八個位置有超過三個活細胞,則該位置活細胞死亡;
- 如果死細胞周圍正好有三個活細胞,則該位置死細胞復活;
根據當前狀態,寫一個函數來計算面板上所有細胞的下一個(一次更新後的)狀態。下一個狀態是通過將上述規則同時應用於當前狀態下的每個細胞所形成的,其中細胞的出生和死亡是同時發生的。
進階:
- 你可以使用原地算法解決本題嗎?請注意,面板上所有格子需要同時被更新:你不能先更新某些格子,然後使用它們的更新後的值再更新其他格子。
- 本題中,我們使用二維數組來表示面板。原則上,面板是無限的,但當活細胞侵佔了面板邊界時會造成問題。你將如何解決這些問題?
思路分析
輔助數組
因爲面板上所有的格子都是一個整體,要變就一起變。所以用一個相同大小的數組存儲更新後的值,最後將值賦給原數組。
具體的邏輯題目中描述得很清楚,看下面具體代碼也行。
原地解法-two bits狀態機
評論區學到的。
- 利用一個 two bits 的狀態機來記錄細胞狀態, 第一位表示下一狀態, 第二位表示當前狀態:
- 00: dead (next state) <- dead (current state)
- 01: dead (next state) <- live (current state)
- 10: live (next state) <- dead (current state)
- 11: live (next state) <- live (current state) - 初始情況對應就是 00 和 01 (默認下一狀態是 dead state)
- 統計每個位置周圍的 live 細胞數決定高位置 1 (live)還是 0 (dead)
- 最後右移一位即爲最終狀態, 注意不需要考慮 01 以及 00 的情況, 因爲已經默認下一狀態爲 dead.
生命遊戲的模擬,可以去玩一玩這個:生命遊戲
代碼實現
/**
* 輔助數組解法
* @param board
*/
public static void gameOfLife(int[][] board) {
//用一個數組存儲改變後的數組數組
if (board == null || board.length == 0 || board[0].length == 0) {
return;
}
int row = board.length, col = board[0].length;
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
matrix[i][j] = changeStatus(board, row, col, i, j, board[i][j] == 1 ? true : false);
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = matrix[i][j];
}
}
}
static int[] dx = {-1, -1, -1, 0, 0, 1, 1, 1};
static int[] dy = {-1, 0, 1, -1, 1, -1, 0, 1};
public static int changeStatus(int[][] board, int row, int col, int i, int j, boolean alive) {
int sum = 0;
for (int k = 0; k < 8; k++) {
sum += count(board, row, col, dx[k] + i, dy[k] + j);
}
//原來爲活細胞
if (alive && (sum < 2 || sum > 3)) {
return 0;
} else if (alive && (sum == 2 || sum == 3)) {
return 1;
} else if (!alive && sum == 3) {
//原來爲死細胞
return 1;
}
return 0;
}
public static int count(int[][] board, int row, int col, int i, int j) {
if (i < 0 || i >= row || j < 0 || j >= col) {
return 0;
}
return board[i][j];
}
/**
* 原地解法
* 用 2比特 的狀態機記錄細胞狀態
* 第一位表示下一狀態,第二位表示當前狀態
*
* @param board
*/
public static void gameOfLife2(int[][] board) {
//用一個數組存儲改變後的數組數組
if (board == null || board.length == 0 || board[0].length == 0) {
return;
}
int row = board.length, col = board[0].length;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
int count = 0;
for (int k = 0; k < 8; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x < 0 || x >= row || y < 0 || y >= col) {
continue;
}
count += (board[x][y] & 1);
}
//活 -> 活
if ((board[i][j] & 1) == 1) {
if (count >= 2 || count <= 3) {
board[i][j] = 0b11;//3
}
//死 -> 活
} else if (count == 3) {
board[i][j] = 0b10;//2
}
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] >>= 1;
}
}
}