LeetCode-289. Game of Life

Description

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

Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, 
vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

1.Any live cell with fewer than two live neighbors dies, as if caused by under-population.
2.Any live cell with two or three live neighbors lives on to the next generation.
3.Any live cell with more than three live neighbors dies, as if by over-population..
4.Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

Write a function to compute the next state (after one update) of the board given its current state.

Follow up:

1.Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first 
and then use their updated values to update other cells.
2.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 the border of the array. How would you address these problems?

Solution 1(C++)

class Solution {
    vector<int> dire={-1, 0, 1};
public:
    int neighLiveNums(vector<vector<int>>& board, int x, int y){
        int cul=board.size();
        int row=board[0].size();
        int cur=0;
        for(int i=0; i<3; i++){
            for(int j=0; j<3; j++){
                if(x+dire[i]<0 || x+dire[i]>=cul || y+dire[j]<0 || y+dire[j]>=row || (dire[i]==0 && dire[j]==0))
                    continue;
                if(board[x+dire[i]][y+dire[j]]) cur++;
            }
        }
        return cur;
    }

    void gameOfLife(vector<vector<int>>& board){
        if(!board.size() || !board[0].size()) return;
        vector<vector<int>> temp(board);
        for(int i=0; i<temp.size(); i++){
            for(int j=0; j<temp[0].size(); j++){
                int cur=neighLiveNums(temp, i, j);
                if(board[i][j]==0 && cur==3) board[i][j]=1;
                if(board[i][j]==1 && (cur==2||cur==3)) board[i][j]=1;
                else board[i][j]=0;
            }
        }
        return;
    }
};

Solution 2(C++)

class Solution {
public:
    int getLiveNeighbours(vector<vector<int>>& board, size_t i, size_t j)
    {
        int count = 0;
        bool has_left = i > 0;
        bool has_up = j > 0;
        bool has_right = i < board.size() - 1;

        if (has_left)
            count += board[i-1][j];
        if (has_up)
            count += board[i][j-1];
        if (has_right)
            count += board[i+1][j];
        if (has_down)
            count += board[i][j + 1];
        if (has_left && has_up)
            count += board[i-1][j-1];
        if (has_left && has_down)
            count += board[i-1][j+1];
        if (has_right && has_up)
            count += board[i+1][j-1];
        if (has_right && has_down)
            count += board[i+1][j+1];

        return count;
    }

    void gameOfLife(vector<vector<int>>& board) {
        //if (!board.empty() && !board[0].empty())
        //    return;
        vector<vector<int>> dup_board = board;
        for(size_t i=0; i < board.size(); i++)
        {
            for (size_t j=0; j < board[i].size(); j++)
            {
                auto ln = getLiveNeighbours(dup_board, i, j);
                if (ln == 3)
                    board[i][j] = 1;
                else if (ln < 2)
                    board[i][j] = 0;
                else if (ln > 3)
                    board[i][j] = 0;
            }
        }
    }
};

Solution 3(C++)

class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        int m = board.size(), n = m ? board[0].size() : 0;
        for (int i=0; i<m; ++i) {
            for (int j=0; j<n; ++j) {
                int count = 0;
                for (int I=max(i-1, 0); I<min(i+2, m); ++I)
                    for (int J=max(j-1, 0); J<min(j+2, n); ++J)
                        count += board[I][J] & 1;
                if (count == 3 || count - board[i][j] == 3)
                    board[i][j] |= 2;
            }
        }
        for (int i=0; i<m; ++i)
            for (int j=0; j<n; ++j)
                board[i][j] >>= 1;
    }
};

算法分析

解法一是自己写的,我本来以为复杂度很高,其实还好,所以遇到算法题,做多了就有一种习惯那就是总想找到最合适的算法,其实有些时候,用有效够快速的算法实现就好。

解法二是解法一的相同思想,但是代码实现更有意思。尤其是找到一个元素周围八个元素的时候,解法一是传统的思想去遍历,但解法二没有,可以学习学习。

解法三更有意思了。解法三就是为了解决题目要求的不适用额外空间。但是按照目前解法一与解法二的思想,都是需要使用新的空间来用board的副本来计算,因为board的元素会在逐步判断的时候进行更新。但是解法三就注意到了一点,那就是board中的元素只有0与1两个状态,这就是bit呀,所以为了解决之前的问题,我们可以将这解法一与解法二中的两个矩阵“合并”为一个。

0变00,1变01。那么高位就储存更新后的值,地位就储存原来的值。那么更新后的元素要取原来的值,就可以:

board[I][J] & 1;    //1其实是01。0或1与0 & 都为0,这样就提取低位了。

然后,由于在查询周围cell存活状态时,并没有跳过自己。所以如果board[i][j]=1,那么就会多加1。如果board[i][j]=0,那么就不会有影响。那么下一个状态存活只有2、4条件满足。所以有:

if (count == 3 || count - board[i][j] == 3)
    board[i][j] |= 2;

其实这个条件我觉得非常有意思,尤其是第二个判断条件,其实暗含两句话:

  • 当board[i][j]=0,count=3;
  • 当board[i][j]=1,count=4.

而不能直接判断count==4,我什么时候才能写出这样的代码呀。。。

然后,在遍历一次board,将高位的值赋给元素,所以有:

for (int i=0; i<m; ++i)
    for (int j=0; j<n; ++j)
        board[i][j] >>= 1;

程序分析

猜你喜欢

转载自blog.csdn.net/zy2317878/article/details/80187236