289. 生命游戏 - 力扣(LeetCode)

题目描述

根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞具有一个初始状态 live(1)即为活细胞, 或 dead(0)即为死细胞。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

  1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
  2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
  3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
  4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;

根据当前状态,写一个函数来计算面板上细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。

示例:

输入: 
[
  [0,1,0],
  [0,0,1],
  [1,1,1],
  [0,0,0]
]
输出: 
[
  [0,0,0],
  [1,0,1],
  [0,1,1],
  [0,1,0]
]

进阶:

  • 你可以使用原地算法解决本题吗?请注意,面板上所有格子需要同时被更新:你不能先更新某些格子,然后使用它们的更新后的值再更新其他格子。
  • 本题中,我们使用二维数组来表示面板。原则上,面板是无限的,但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题?

题解1

如果不采用原地算法,可以新建一个tmp数组来存储更新后的状态,最后再将tmp赋值给board。

代码1

/*
不采用原地算法
时间复杂度:O(n^2)
空间复杂度:O(n)
*/
class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        int m = board.size();
        int n = board[0].size();
        //定义8个方向
        int dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}};
        vector<vector<int>> tmp(m, vector<int>(n, 0));

        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 + dir[k][0];
                    int y = j + dir[k][1];
                    if(x >= 0 && x < m && y >= 0 && y < n && board[x][y]){
                        ++cnt;
                    }
                }
                if(board[i][j]){//活细胞
                    if(cnt < 2 || cnt > 3){//周围活细胞数目少于2或大于3,细胞死亡
                        tmp[i][j] = 0;
                    }
                    else{//活细胞数目为2或3时,存活
                        tmp[i][j] = 1;
                    }
                }
                else{//死细胞
                    if(cnt == 3){//周围活细胞数目刚好为3时,复活
                        tmp[i][j] = 1;
                    }
                    else{//仍然是死亡状态
                        tmp[i][j] = 0;
                    }
                }
            }
        }
        board = tmp;//将更新后的状态赋值给board
    }
};

题解2

很多需要标记的原地算法都是用自身元素以某种方式进行信息携带。

这里细胞状态为0和1,我们可以将低位保持不变(用位与便能获得);然后高位存储细胞周围活细胞的数目(右移一位即可获得)。

当然除了2进行,采用十进制也可以,高位信息(除以10),低位信息(mod10),但是这样的运算速度显然没有位运算快。

代码2

/*
采用原地算法
用自身做标记
最低位记录的是细胞的状态
高位记录的是细胞周围活细胞的数目
时间复杂度:O(n^2)
空间复杂度:O(n)
*/
class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        int m = board.size();
        int n = board[0].size();
        //定义8个方向
        int dir[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -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 + dir[k][0];
                    int y = j + dir[k][1];
                    if(x >= 0 && x < m && y >= 0 && y < n && (board[x][y] & 1)){
                        ++cnt;
                    }
                }
                board[i][j] = (cnt << 1) | board[i][j];
            }
        }

        //更新细胞状态
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                int status = board[i][j] & 1;//获取当前细胞状态
                int cnt = board[i][j] >> 1;//获取周围活细胞的数目
                if(status){
                    if(cnt < 2 || cnt > 3){
                        board[i][j] = 0;
                    }
                    else{
                        board[i][j] = 1;
                    }
                }
                else{
                    if(cnt == 3){
                        board[i][j] = 1;
                    }
                    else{
                        board[i][j] = 0;
                    }
                }
            }
        }
    }
};
发布了169 篇原创文章 · 获赞 35 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/happyeveryday62/article/details/104192436