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

来源:互联网 发布:淘宝sy仓什么意思 编辑:程序博客网 时间:2024/06/05 19:02

Game of Life

原题链接 Game of Life

这里写图片描述
生命游戏,计算下一个状态。游戏规则如下

  • 对于一个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),但是如何记录,需要仔细考虑