每天一道LeetCode-----生命游戏

Game of Life

原题链接 Game of Life

每天一道LeetCode-----生命游戏_第1张图片
生命游戏,计算下一个状态。游戏规则如下

  • 对于一个live的细胞,如果它周围live的细胞数量少于两个,那么它将dead
  • 对于一个live的细胞,如果它周围live的细胞数量是两个或三个,那么它继续live
  • 对于一个live的细胞,如果它周围live的细胞数量超过三个,那么它将dead
  • 对于一个dead的细胞,如果它周围live的细胞数量恰好是三个,那么它将live

给定某一时刻各个细胞的状态,用二维数组表示,1表示live,0表示dead。需要返回下一个状态,要求只能在原数组中更改,即空间复杂度在O(n)

不过有一点需要主要,每个细胞周围有8个细胞,判断这个细胞下一个状态依据的是它周围那么细胞的状态。比说如当前状态为A,下一个状态为B。那么对于某个处于状态A的细胞而言,要判断它下一个状态,依据的是它周围8个细胞的A状态,而不是B状态。
这就要求不能一边遍历一边修改,只能先遍历一遍,记录好哪些细胞需要改变状态。然后在第二遍遍历的时候更新状态。


记录的方式比较多,比如

增加几个细胞的状态,如2代表现在是live并且下一个状态也是live;3代表现在是live但是下一个状态是dead;4代表现在是dead但是下一个状态是live

这样,所有细胞当前的状态和下一个状态就都知道了,在第二次遍历时,只需要根据2,3,4做适当的修改即可

代码如下

class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        //2 : current is live and next is live
        //3 : current is live but next is dead
        //4 : current is dead but next is live

        if(board.empty() || board[0].empty())
            return;
        int m = board.size();
        int n = board[0].size();
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                int liveCount = 0;
                /* 判断周围live的细胞数量 */
                for(int p = std::max(0, i - 1); p < std::min(m, i + 2); ++p)
                {
                    for(int q = std::max(0, j - 1); q < std::min(n, j + 2); ++q)
                    {
                        if(p == i &&  q == j)
                            continue;
                        if(isLive(board, p, q))
                            ++liveCount;
                    }
                }

                /* 根据周围细胞数量改变状态 */
                if(liveCount < 2 && isLive(board, i, j))
                    board[i][j] = 3;
                else if(liveCount > 3 && isLive(board, i, j))
                    board[i][j] = 3;
                else if(liveCount == 3 && !isLive(board, i, j))
                    board[i][j] = 4;
            }
        }

        /* 第二次遍历时改变状态 */
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                if(board[i][j] == 3)
                    board[i][j] = 0;
                else if(board[i][j] == 4)
                    board[i][j] = 1;
            }
        }
    }

private:
    /* 一个细胞处于live的条件是当前状态是live而不是下一个状态是live */
    bool isLive(vector<vector<int>>& board, int i, int j)
    {
        return board[i][j] == 1 || board[i][j] == 2 || board[i][j] == 3;
    }
};

除了上面提到的增加细胞状态的方法外,还有一种方法可以记录细胞的当前状态和下一个状态。
因为最开始,细胞只有0和1两种状态,只需要一个字节即可记录,也就是说int类型的其它位置实际上是用不上的。所以,可以将int类型其它字节用上,来记录细胞的下一个状态,不过只需要一个字节即可。

0和1的二进制码分别是0000和0001,表示细胞处于dead状态和live状态。如果试图将下一个状态也记录在这里面,就像

  • 0000表示当前状态是dead并且下一个状态是dead,值为0
  • 0010表示当前状态是dead并且下一个状态是live,值为2
  • 0001表示当前状态是live并且下一个状态是dead,值为1
  • 0011表示当前状态是live并且下一个状态是live,值为3

这样,在第二遍遍历时只需要将每个细胞的值右移一位,就从当前状态变为下一个状态了。

代码如下

class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        if(board.empty() || board[0].empty())
            return;
        int m = board.size();
        int n = board[0].size();
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                int liveCount = 0;
                for(int p = std::max(0, i - 1); p < std::min(m, i + 2); ++p)
                {
                    for(int q = std::max(0, j - 1); q < std::min(n, j + 2); ++q)
                    {
                        /* 这里没有排除p == i && q == j */
                        liveCount += (board[p][q] & 1);
                    }
                }
                /* 对于live的细胞,它下一个状态仍然是live的条件是算上它本身,周围live的细胞数量是3或4个 */
                /* 对于dead的细胞,它下一个状态是live的条件是算上它本身,周围live的细胞数量是3个 */
                /* 不能直接liveCount == 4因为对于dead的细胞是错误的 */
                if(liveCount == 3 || liveCount - 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;
            }
        }
    }
};

本题和每天一道LeetCode—–给定一个矩阵,如果某个元素是0,就将所在行所在列上所有元素否置0类似,都是在第一遍遍历时在原二维数组上做记录,在第二遍遍历时集中更改。从而保证空间复杂度为O(1),但是如何记录,需要仔细考虑

你可能感兴趣的:(LeetCode)