题目描述
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
样例描述
思路
方法一:额外数组标记要修改的位置,最后统一改变 (可优化为正负数判断)
注意扫描周围8个位置用循环时,要避开本身的位置
优化:原地算法不需要额外的标记数组,如果是活的变死了,就设置成负数-1(最好用-1,这样判断呼活细胞时,用绝对值等于1即可),如果是死的变活了,就设置成2,最后扫描根据数来进行变化即可。 注意这样改变的话,可能会影响到后面的判断,所以设置数的时候要尽可能好判断。
方法二:位运算 + 原地算法
- 由于原数组只用到了最后一位,因此可以存在倒数第二位进行变化。
- 判断活细胞时,要用&1取倒数第一位来判断,因为经过填充倒数第二位,可能是11了,所以不能判断是否等于1来判断是否是活细胞。
代码
方法一:
class Solution {
public void gameOfLife(int[][] board) {
int m = board.length, n = board[0].length;
boolean flag[][] = new boolean[m][n];
for (boolean f[]: flag) {
Arrays.fill(f, false);
}
for (int i = 0; i < m; i ++ ) {
for (int j = 0; j < n; j ++ ) {
//扫描周围活细胞,死细胞个数
int live = 0, dead = 0;
for (int x = i - 1; x <= i + 1; x ++ ) {
for (int y = j - 1; y <= j + 1; y ++ ) {
//自己不能算进去
if (x == i && y == j) continue;
if (x >= 0 && x < m && y >= 0 && y < n) {
if (board[x][y] == 1) live ++;
else dead ++;
}
}
}
//如果是活细胞
if (board[i][j] == 1) {
//如果周围活细胞数小于2个或者大于3个
if (live < 2 || live > 3) {
flag[i][j] = true;
}
}
//如果是死细胞
else {
if (live == 3) {
flag[i][j] = true;
}
}
}
}
//扫描所有需要改变的,将0改1,或者1改0
for (int i = 0; i < m; i ++ ) {
for (int j = 0; j < n; j ++ ) {
if (flag[i][j] == true) {
if (board[i][j] == 1) {
board[i][j] = 0;
}
else board[i][j] = 1;
}
}
}
}
}
方法一优化
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 live = 0;
for (int x = i - 1; x <= i + 1; x ++ ) {
for (int y = j - 1; y <= j + 1; y ++ ) {
//自己不能算进去
if (x == i && y == j) continue;
if (x >= 0 && x < m && y >= 0 && y < n) {
//一定要带绝对值,因为有可能活细胞变死了成了-1
if (Math.abs(board[x][y]) == 1) live ++;
}
}
}
//如果是活细胞
if (board[i][j] == 1) {
//如果周围活细胞数小于2个或者大于3个
if (live < 2 || live > 3) {
board[i][j] = -1;
}
}
//如果是死细胞
else {
if (live == 3) {
board[i][j] = 2;
}
}
}
}
//扫描所有需要改变的,将0改1,或者1改0
for (int i = 0; i < m; i ++ ) {
for (int j = 0; j < n; j ++ ) {
if (board[i][j] > 0) {
board[i][j] = 1;
} else {
board[i][j] = 0;
}
}
}
}
}
方法二:
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 live = 0;
for (int x = i - 1; x <= i + 1; x ++ ) {
for (int y = j - 1; y <= j + 1; y ++ ) {
//等于本身就跳过
if (x == i && y == j) continue;
if (x >= 0 && x < m && y >= 0 && y < n) {
//这里判断得用&,不能用等于,因为更新了倒数第二位,就已经不等于1了
if ((board[x][y] & 1) == 1) {
live ++;
}
}
}
}
//当前细胞状态,以及下一个状态
int cur = board[i][j] & 1, next = 0;
if (cur == 1) {
if (live < 2 || live > 3) {
next = 0;
}else next = 1;
}
else {
if (live == 3) {
next = 1;
}
}
//将倒数第二位设置为新的状态
//或运算 + 左移
board[i][j] |= (next << 1);
}
}
//最后,将所有倒数第二位移动到倒数第一位
for (int i = 0; i < m; i ++ ) {
for (int j = 0; j < n; j ++) {
board[i][j] >>= 1;
}
}
}
}