289. Life of Game

237 查看

问题:
According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970."

Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

Any live cell with fewer than two live neighbors dies, as if caused by under-population.
Any live cell with two or three live neighbors lives on to the next generation.
Any live cell with more than three live neighbors dies, as if by over-population..
Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
Write a function to compute the next state (after one update) of the board given its current state.

Follow up:
Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells.
In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems?

解答:
我的解法是需要最多o(mn)的空间的。

class Point {
    int x, y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public void checkAlive(int[][] board, int val, int i, int j, List<Point> list) {
    int count = 0;
    int[][] dir = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
    for (int k = 0; k < dir.length; k++) {
        int x = i + dir[k][0], y = j + dir[k][1];
        if (x < 0 || x > board.length - 1 || y < 0 || y > board[0].length - 1) continue;
        count += board[x][y];
    }
    if (val == 1 && (count < 2 || count > 3)) {
        list.add(new Point(i, j));
    }
    if (val == 0 && count == 3) {
        list.add(new Point(i, j));
    }
}

public void gameOfLife(int[][] board) {
    if (board == null || board.length == 0 || board[0].length == 0) return;
    int m = board.length, n = board[0].length;
    List<Point> list = new ArrayList<Point>();
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            checkAlive(board, board[i][j], i, j, list);
        }
    }
    for (int i = 0; i < list.size(); i++) {
        Point p = list.get(i);
        board[p.x][p.y] = board[p.x][p.y] == 1 ? 0 : 1;
    }
}

如果要做到in space, 那么我看到discussion里一个非常tricky的做法是用一个2-bit的数表示改变前和改变后的状态。改变前有两个状态:00, 01. 在满足一定的条件后,改变前一个bit的状态也有两种:10, 11.所以我们只要记录改变的状态,然后将实际的数向后移动一位就可以得到当然的一状态。

public int helper(int[][] board, int i, int j) {
    int count = 0;
    int[][] dir = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
    for (int k = 0; k < dir.length; k++) {
        int x = i + dir[k][0], y = j + dir[k][1];
        if (x < 0 || x > board.length - 1 || y < 0 || y > board[0].length - 1) continue;
        count += board[x][y] & 1;
    }
    return count;
}
public void gameOfLife(int[][] board) {
    if (board == null || board.length == 0 || board[0].length == 0) return;
    int m = board.length, n = board[0].length;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            int lives = helper(board, i, j);
            if (board[i][j] == 1 && (lives == 2 || lives == 3)) {
                board[i][j] = 3;
            }
            if (board[i][j] == 0 && lives == 3) {
                board[i][j] = 2;
            }
        }
    }
    
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            board[i][j] >>= 1;
        }
    }
}