LeetCode 289. Game of Life
准备dropbox的时候又仔细看了看这道题。本题的难点一是in place,二是如果board很大或者无限大怎么办。
Naive
class Solution { public: int dirs[8][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}}; void gameOfLife(vector<vector<int>>& board) { int m=board.size(), n=board[0].size(); vector<vector<int>> copy(board); for (int i=0;i<m;++i){ for (int j=0;j<n;++j){ // update board[i][j] int count=0; for (auto dir:dirs){ int ii=i+dir[0], jj=j+dir[1]; if (ii<0 || ii>=m || jj<0 || jj>=n) continue; if (copy[ii][jj]==1) ++count; } if (copy[i][j]==1){ if (count<2 || count>3) board[i][j]=0; if (count==2 || count==3) board[i][j]=1; }else{ if (count==3) board[i][j]=1; else board[i][j]=0; } } } } };
In Place
由于状态用一位表示即可,因此利用两位(two bits)就可以同时记录之前的状态和新状态,进而不用新建一个board。
低位是原来的状态,高位是新状态。
class Solution { public: int dirs[8][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}}; void gameOfLife(vector<vector<int>>& board) { int m=board.size(), n=board[0].size(); for (int i=0;i<m;++i){ for (int j=0;j<n;++j){ // update higher bit of board[i][j] int count=0; for (auto dir:dirs){ int ii=i+dir[0], jj=j+dir[1]; if (ii<0 || ii>=m || jj<0 || jj>=n) continue; if (board[ii][jj]&1==1) ++count; } if (board[i][j]&1==1){ if (count<2 || count>3) board[i][j]=1; // 01 if (count==2 || count==3) board[i][j]=3; // 11 }else{ if (count==3) board[i][j]=2; // 10 else board[i][j]=0; // 00 } } } for (int i=0;i<m;++i){ for (int j=0;j<n;++j){ board[i][j] >>= 1; } } } };
Large & Sparse
如果board非常大,那么保存全部的格子十分浪费空间。我们可以只保存live的格子,然后更新neighbor。然后根据规则,得到一轮后新的所有live。下面代码核心部分是 gameOfLifeSparse,剩下的包裹函数用来通过test case的。
这种方法的缺陷是,如果board无限大,我们无法得到所有live格子,内存可能也不够放下。
class Solution { public: struct pair_hash{ template <class T1, class T2> std::size_t operator()(std::pair<T1, T2> const &pair) const { std::size_t h1 = std::hash<T1>()(pair.first); std::size_t h2 = std::hash<T2>()(pair.second); return h1 ^ h2; } }; int dirs[8][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}}; void gameOfLife(vector<vector<int>>& board) { int m=board.size(), n=board[0].size(); unordered_set<pair<int,int>,pair_hash> live; for (int i=0;i<m;++i){ for (int j=0;j<n;++j){ if (board[i][j]==1) live.insert({i,j}); } } live = gameOfLifeSparse(live); for (int i=0;i<m;++i){ for (int j=0;j<n;++j){ if (live.count({i,j})) board[i][j] = 1; else board[i][j] = 0; } } } unordered_set<pair<int,int>,pair_hash> gameOfLifeSparse(unordered_set<pair<int,int>,pair_hash> &live){ unordered_map<pair<int,int>,int,pair_hash> count; for (pair<int,int> x:live){ int i=x.first, j=x.second; for (auto dir:dirs){ int ii=i+dir[0], jj=j+dir[1]; ++count[{ii,jj}]; } } unordered_set<pair<int,int>,pair_hash> new_live; for (auto x:count){ if (x.second==3 || x.second==2 && live.count(x.first)) new_live.insert(x.first); } return new_live; } };
Infinite
假定board信息都保存在文件里,每次处理三行的中间一行。处理完后删除第一行,从文件新读入一行。
https://leetcode.com/problems/game-of-life/discuss/73217/Infinite-board-solution/201780
class Solution { public: int dirs[8][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}}; void gameOfLifeInfinite(filename) { ifstream in(filename); string line; // deal with the first row and write back vector<string> threeLineBoard(3); for (int i=0;i<3;++i){ getline(in,line); threeLineBoard.push_back(line); } while (true){ for (int i=0;i<threeLineBoard[1].size();++i){ // update the middle row in place // write the updated middle row back to file, we can calculate the position // e.g. write to ith line, then go to i*(lengh of row) using file.seekg() } threeLineBoard.erase(threeLineBoard.begin()); getline(in,line); if (line=="") break; threeLineBoard.push_back(line); } // deal with the last row and write back } };

浙公网安备 33010602011771号