LeetCode_Array_289. Game of Life 生命游戏(C++/Java)【原地算法】

目录

一,题目描述

英文描述

中文描述

二,解题思路

三,AC代码

C++

Java

四,解题过程

第一搏

第二搏


一,题目描述

原题链接289. 生命游戏

英文描述

According to 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."

The board is made up of an m x n grid of cells, where each cell has an initial state: live (represented by a 1) or dead (represented by a 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.
The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously. Given the current state of the m x n grid board, return the next state.

 

Example 1:


Input: board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
Output: [[0,0,0],[1,0,1],[0,1,1],[0,1,0]]

Example 2:


Input: board = [[1,1],[1,0]]
Output: [[1,1],[1,1]]

Constraints:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 25
  • board[i][j] is 0 or 1.
     

Follow up:

  • Could you solve it in-place? Remember that the board needs to be updated simultaneously: 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 upon the border of the array (i.e., live cells reach the border). How would you address these problems?

中文描述

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

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

如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

 

示例 1:


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

示例 2:


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

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 25
  • board[i][j] 为 0 或 1
     

进阶:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/game-of-life
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二,解题思路

参考@力扣官方题解【生命游戏】

添加新状态2(当前存活,更新后死亡)、-1(当前死亡,更新后存活)。这样,在遍历完该方格周围细胞后即可直接更新状态;

  • 只需要将判断细胞存活的条件从 board[i][j] == 1 改为 board[i][j] >= 1;
  • 在遍历完数组之后,再次遍历,将状态为-1、2的细胞分别更新为1、0即可;

三,AC代码

C++

class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        for(int i = 0; i < board.size(); i++) {
            for(int j = 0; j < board[0].size(); j++) {
                int numOfLive = getNumOfLive(i, j, board);
                if(board[i][j]) {
                    // 定义新状态2:当前为活细胞,但更新后为死细胞
                    if(numOfLive < 2 || numOfLive > 3) board[i][j] = 2;
                } else {
                    // 定义新状态-1:当前为死细胞,但更新后为活细胞
                    if(numOfLive == 3) board[i][j] = -1;
                }
            }
        }
        // 再次遍历,确定最终状态
        for(int i = 0; i < board.size(); i++) {
            for(int j = 0; j < board[0].size(); j++) {
                if(board[i][j] > 1) board[i][j] = 0;
                else if(board[i][j] < 0) board[i][j] = 1;
            }
        }
    }
    // 获取每个细胞周围活细胞的数量
    int getNumOfLive(int x, int y, vector<vector<int>>& board) {
        int num = 0, row = board.size(), col = board[0].size();
        for(int i = -1; i < 2; i++) {
            int posX = x + i;
            for(int j = -1; j < 2; j++) {
                int posY = y + j;
                // 当前细胞的位置不参加计数
                if(i == 0 && j == 0) continue;
                // 超出边界的位置不参加计数
                if(posX < 0 || posX >= row || posY < 0 || posY >= col) continue;
                // 状态1、2均算作活细胞,计数加一
                if(board[posX][posY] >= 1) num++;
            }
        }
        return num;
    }
};

Java

class Solution {
    public void gameOfLife(int[][] board) {
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                int numOfLive = getNumOfLive(i, j, board);
                if(board[i][j] == 1) {
                    // 定义新状态2:当前为活细胞,但更新后为死细胞
                    if(numOfLive < 2 || numOfLive > 3) board[i][j] = 2;
                } else {
                    // 定义新状态-1:当前为死细胞,但更新后为活细胞
                    if(numOfLive == 3) board[i][j] = -1;
                }
            }
        }
        // 再次遍历,确定最终状态
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[0].length; j++) {
                if(board[i][j] > 1) board[i][j] = 0;
                else if(board[i][j] < 0) board[i][j] = 1;
            }
        }
    }
    public int getNumOfLive(int x, int y, int[][] board) {
        int num = 0, row = board.length, col = board[0].length;
        for(int i = -1; i < 2; i++) {
            int posX = x + i;
            for(int j = -1; j < 2; j++) {
                int posY = y + j;
                // 当前细胞的位置不参加计数
                if(i == 0 && j == 0) continue;
                // 超出边界的位置不参加计数
                if(posX < 0 || posX >= row || posY < 0 || posY >= col) continue;
                // 状态1、2均算作活细胞,计数加一
                if(board[posX][posY] >= 1) num++;
            }
        }
        return num;
    }
}

 

四,解题过程

第一搏

看见原地算法,脑海中浮现了两个技巧——动态规划、滑动窗口。【综合实现复杂度及优化空间考虑,证明不可行】


一是动态规划(是指求当前细胞周围活细胞数目的方法)。假如按照自左向右的方法,计算当前细胞与下一个细胞附近活细胞数目有部分重合

那么是不是可以在这个基础上进行更新,比如求黄色大方格活细胞个数,目前已知紫色方格活细胞数目,只需要记录绿色方格中活细胞个数,并遍历蓝色方格即可

           

这么算下来原先需要遍历的8个位置变成了3个,确实能够减少一定的重复工作。但是需要考虑到较多的边界问题,比如初始条件设置?按照S顺序遍历?增加了编程的复杂度,不是很好的选择。


二是滑动窗口(指按照一定顺序遍历所有细胞)。

想着在最上面多套两层

在遍历当前细胞的时候,将该位置下一次更新结果放在上一层中心方格的地方

这样更新结束后,将上两行的数据平移下来即可。

然而这样需要额外占用O(N)的空间(假设规格是m*n),而且最后还需要整体平移数据,这种读写开销属实承担不起。

第二搏

官方给出的解题思路是增加状态。比如原先只有两种状态0(死亡)、1(存活)。

现在增加两种状态2(当前存活,更新后死亡)、-1(当前死亡,更新后存活)。

这样就可以放心的在原地进行修改数据了:

  • 只需要将判断细胞存活的条件从 board[i][j] == 1 改为 board[i][j] >= 1;
  • 在遍历完数组之后,再次遍历,将状态为-1、2的细胞分别更新为1、0即可;

兄弟们,把666扣在公屏上!


用Java重写了一遍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值