题目描述
根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/game-of-life
示例 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]]
这里描述的是细胞的死亡和存活,每个细胞都是以当前的状态为准,就是说当前周围的细胞数量为准,而不是前一个改变之后,会影响
当下这个细胞,每个细胞都可以保存周围的信息,他们一起计算出各自的结果,之后,在一次进入到下一个状态。
这里最简单的做法就是拿另一个矩阵进行存储,这样的存储的话,只需要把每个细胞的状态都遍历到,那么得出结果,进行下一次复制即可
1.周围的8个位置,不含有当前的(i,j),而且不能越界超过m和n还有0,可以使用遍历找到这几个位置
2.将temp遍历计算给board中
空间复杂度是O(mn),时间复杂度是O(mn3^2)
这里上一轮需要依赖上一轮的细胞状态,来更新下一轮
void gameOfLife(vector<vector<int>>& board) {
int m = board.size();
int n = board[0].size();
vector<vector<int>> temp(m, vector<int>(n));
//这里是将board的值都复制到对应temp数组中
//直接利用temp计算出的值,直接复制到board中
temp = board;
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
//这里检查temp[i][j]周围存活的元素个数
int sum = 0;
//这里可以使用neighbors,其中[-1,0,1]三种状态来改变i和j
//利用for循环检查周围的8个元素加上本身的元素
for(int i1 = i - 1; i1 <= i + 1; i1++) {
for(int j1 = j - 1; j1 <= j + 1; j1++) {
if(i1 >= 0 && i1 < m && j1 >= 0 && j1 < n) {
sum += temp[i1][j1];
}
}
}
//这里去掉本身的元素
sum -= temp[i][j];
//这里temp和board的默认细胞状态是相同的,那么下面需要更改细胞状态的时候才需要更新board数组,其他时候可以不更新board
//这里判断当前是活细胞还是死细胞
if(temp[i][j]) {
if(sum == 2 || sum == 3) { //对于活细胞相应状态变化
board[i][j] = 1;
} else {
board[i][j] = 0;
}
} else {
if(sum == 3) { //对于死细胞相应状态的变化
board[i][j] = 1;
} else {
board[i][j] = 0;
}
}
//上面的判断可以写成下面的形式
//也就是只有这两种情况下,他是存活的,其他情况下都是死亡的
//通过验证了,没问题。
//if((temp[i][j] && (sum == 2 || sum == 3)) || (!temp[i][j] && sum == 3)) {
// board[i][j] = 1;
//} else {
// board[i][j] = 0;
//}
//也可以写成这种的
//if (temp[i][j]&&(sum < 2 || sum >3 )) {
// board[i][j] = 0;
//}
//if (!temp[i][j]&&sum==3) {
// board[i][j] = 1
//}
}
}
}
这里使用原地逆置的方法。
1.思路是这样的,因为这里如果0和1的状态改变了,那么在计算下一个位置的时候,就会改变对应的结果
所以,这里除了0,1定义多个状态,能表示改变之前的状态和改变之后的状态。
2.这里原来的0,1表示的之前和之后的状态是一致的,都是死亡和存活
3.定义-1为原来是1,现在是0,而2表示原来是0,现在是1
4.这样的话,我们在遍历周围的9个点的时候,就需要做对应的判断找出之前的状态
5.而当所有的都已经更新完成之后,我们还需要在遍历数组,将更新到之后的状态。
void gameOfLife(vector<vector<int>>& board) {
int m = board.size();
int n = board[0].size();
//更新对应的board数组
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
int sum = 0;
//遍历对应的8个位置
for(int i1 = i - 1; i1 <= i + 1; i1++) {
for(int j1 = j - 1; j1 <= j + 1; j1++) {
if(i1 >= 0 && i1 < m && j1 >= 0 && j1 < n) { //如果在范围内的话
if(board[i1][j1] == 1 || board[i1][j1] == -1) { //对应的四种状态只有这两种状态才能加加
sum += 1;
}
}
}
}
sum -= board[i][j]; //减去自己位置的值,该位置只有两种状态,还未被更新过
//更新当前的状态
if(board[i][j] && (sum < 2 || sum > 3)) {
board[i][j] = -1;
}
if(!board[i][j] && sum == 3) {
board[i][j] = 2;
}
}
}
//将-1和2的状态转换为0,1的状态
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(board[i][j] == -1) {
board[i][j] = 0;
}
if(board[i][j] == 2) {
board[i][j] = 1;
}
}
}
}
int main() {
vector<vector<int>> matrix(4, vector<int>(3));
matrix[0][0] = 0;
matrix[0][1] = 1;
matrix[0][2] = 0;
//matrix[0][3] = 4;
matrix[1][0] = 0;
matrix[1][1] = 0;
matrix[1][2] = 1;
//matrix[1][3] = 8;
matrix[2][0] = 1;
matrix[2][1] = 1;
matrix[2][2] = 1;
//matrix[2][3] = 12;
matrix[3][0] = 0;
matrix[3][1] = 0;
matrix[3][2] = 0;
//matrix[3][3] = 16;
gameOfLife(matrix);
return 0;
}