Leetcode--Java--289. 生命游戏

题目描述

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。

样例描述

在这里插入图片描述

思路

方法一:额外数组标记要修改的位置,最后统一改变 (可优化为正负数判断)
注意扫描周围8个位置用循环时,要避开本身的位置
优化:原地算法不需要额外的标记数组,如果是活的变死了,就设置成负数-1(最好用-1,这样判断呼活细胞时,用绝对值等于1即可),如果是死的变活了,就设置成2,最后扫描根据数来进行变化即可。 注意这样改变的话,可能会影响到后面的判断,所以设置数的时候要尽可能好判断。
方法二:位运算 + 原地算法

  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;
           }
       }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值