|
| 1 | +# Game of Life |
| 2 | + |
| 3 | +According to [Wikipedia's article](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life): "The Game of Life, also known |
| 4 | +simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970." |
| 5 | + |
| 6 | +The board is made up of an m x n grid of cells, where each cell has an initial state: live (represented by a 1) or dead |
| 7 | +(represented by a 0). Each cell interacts with its [eight neighbors](https://en.wikipedia.org/wiki/Moore_neighborhood) |
| 8 | +(horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article): |
| 9 | + |
| 10 | +- Any live cell with fewer than two live neighbors dies as if caused by under-population. |
| 11 | +- Any live cell with two or three live neighbors lives on to the next generation. |
| 12 | +- Any live cell with more than three live neighbors dies, as if by over-population. |
| 13 | +- Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. |
| 14 | + |
| 15 | +The next state of the board is determined by applying the above rules simultaneously to every cell in the current state of the m x n grid board. In this process, births and deaths occur simultaneously. |
| 16 | + |
| 17 | +Given the current state of the board, update the board to reflect its next state. |
| 18 | + |
| 19 | +Note that you do not need to return anything. |
| 20 | + |
| 21 | +## Examples |
| 22 | + |
| 23 | +Example 1: |
| 24 | + |
| 25 | +```text |
| 26 | +Input: board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]] |
| 27 | +Output: [[0,0,0],[1,0,1],[0,1,1],[0,1,0]] |
| 28 | +``` |
| 29 | + |
| 30 | +Example 2: |
| 31 | + |
| 32 | +```text |
| 33 | +Input: board = [[1,1],[1,0]] |
| 34 | +Output: [[1,1],[1,1]] |
| 35 | +``` |
| 36 | + |
| 37 | +## Constraints |
| 38 | + |
| 39 | +- m == board.length |
| 40 | +- n == board[i].length |
| 41 | +- 1 <= m, n <= 25 |
| 42 | +- `board[i][j]` is 0 or 1. |
| 43 | + |
| 44 | +Follow up: |
| 45 | + |
| 46 | +- Could you solve it in-place? Remember that the board needs to be updated simultaneously: You cannot update some cells |
| 47 | + first and then use their updated values to update other cells. |
| 48 | +- In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause |
| 49 | + problems when the active area encroaches upon the border of the array (i.e., live cells reach the border). How would |
| 50 | + you address these problems? |
| 51 | + |
| 52 | +## Topics |
| 53 | + |
| 54 | +- Array |
| 55 | +- Matrix |
| 56 | +- Simulation |
| 57 | + |
| 58 | +## Solution |
| 59 | + |
| 60 | +The core intuition is to update all cells simultaneously without corrupting neighbor counts. We encode transitions in |
| 61 | +place: 1 means alive (and may stay alive), -1 means was alive and will die (underpopulation/overpopulation), 0 means |
| 62 | +dead (and may stay dead), and 2 means was dead and will become alive (reproduction). While counting neighbors, we only |
| 63 | +care about the original state, so we test abs(`board[r][c]) == 1`. This is true for 1 and -1 (originally alive) and false |
| 64 | +for 0 and 2 (originally dead). With that, we finish a full pass marking transitions, then a quick second pass to normalize |
| 65 | +to 0 or 1. |
| 66 | + |
| 67 | +Using the intuition above, we implement the algorithm as follows: |
| 68 | + |
| 69 | +1. Store the board’s dimensions in rows and cols. |
| 70 | +2. Define an array neighbors = {-1, 0, 1}. The Cartesian product {−1,0,1} × {−1,0,1} gives the relative offsets to all |
| 71 | + eight neighboring cells around a given cell. We skip the (0, 0) pair as it represents the cell itself. This allows us |
| 72 | + to systematically check every neighbor’s state when counting alive cells. |
| 73 | +3. Iterate over every cell (row, col) in the grid. For each cell: |
| 74 | + - Initialize a counter liveNeighbors = 0. |
| 75 | + - To count alive neighbors, loop i = 0..2 and j = 0..2 to form a relative offset (neighbors[i], neighbors[j]): |
| 76 | + - Compute the neighbor’s coordinates: r = row + neighbors[i] and c = col + neighbors[j]. |
| 77 | + - If (r, c) is in bounds and abs(``board[r][c]) == 1`` (originally alive), increment liveNeighbors. |
| 78 | + - After counting liveNeighbors, apply the Game of Life rules using the cell’s original state (the current value before |
| 79 | + any normalization): |
| 80 | + - If `board[row][col] == 1` and liveNeighbors is less than 2 (underpopulation) or greater than 3 (overpopulation), |
| 81 | + mark it as -1 to encode alive → dead. |
| 82 | + - Otherwise, if `board[row][col] == 0` and liveNeighbors is 3, mark it as 2 to encode dead → alive (reproduction). |
| 83 | + - Otherwise, leave the cell unchanged (1 stays 1, 0 stays 0). |
| 84 | +4. Iterate through the board again to finalize the next generation by normalizing each cell back to 0 or 1: |
| 85 | + - If `board[row][col]` > 0 (values 1 or 2), set it to 1. |
| 86 | + - Otherwise (for values 0 or -1), set it to 0. |
| 87 | +5. The normalized board now represents the next state after applying the rules simultaneously. |
| 88 | + |
| 89 | + |
| 90 | +### Time complexity |
| 91 | + |
| 92 | +The time complexity is O(m×n), where m and n are the dimensions of the board. Each cell inspects up to 8 neighbors |
| 93 | +(constant work) and is processed twice (marking, then normalization). |
| 94 | + |
| 95 | +### Space complexity |
| 96 | + |
| 97 | +The algorithm’s space complexity is constant, O(1). |
0 commit comments