题目链接:Valid Sudoku有效的数独
题目已经十分确定的说了只有1~9,因此标记法无疑是非常好的选择。
基本思路:对行、列、小数独块分别用一个size为9的数组来标记数字1~9在本行(列/块)中是否已使用。
根据思路则不难写出下面的代码:
public static int startIndex = 0; public static int sudokuSize = 9; public boolean isValidSudoku(char[][] board) { int i; boolean[][] mark = new boolean[sudokuSize][sudokuSize+1]; // 判断所有行是否符合 for (i = startIndex; i < sudokuSize; i++) { for (int j = startIndex; j < sudokuSize; j++) { if (board[i][j] != '.') { int temp = board[i][j] - '0'; if (mark[i][temp]) return false; else mark[i][temp] = true; } } } // 判断所有列是否符合 initMarkArr(mark, false); for (i = startIndex; i < sudokuSize; i++) { for (int j = startIndex; j < sudokuSize; j++) { if (board[j][i] != '.') { int temp = board[j][i] - '0'; if (mark[i][temp]) return false; else mark[i][temp] = true; } } } // 判断所有小数独块是否符合 initMarkArr(mark, false); for (i = startIndex; i < sudokuSize; i++) { for (int j = startIndex; j < sudokuSize; j++) { int index = i/3 * 3 + j/3; if (board[j][i] != '.') { int temp = board[j][i] - '0'; if (mark[index][temp]) return false; else mark[index][temp] = true; } } } return true; } // 初始化数组 public void initMarkArr(boolean[][] arr, boolean value) { for (int j = 0; j < arr.length; j++) { Arrays.fill(arr[j], value); } }
代码看起来有点冗余,我们稍作代码简化,可得如下代码
public boolean isValidSudoku(char[][] board) { boolean[][] markRow = new boolean[9][10]; boolean[][] markCol = new boolean[9][10]; boolean[][] markBlock = new boolean[9][10]; // 多定义两个数组可以省去初始化操作 for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { int index = i/3 * 3 + j/3; if (board[j][i] != '.') { int temp = board[j][i] - '0'; if (markRow[j][temp]) return false; else markRow[j][temp] = true; if (markCol[i][temp]) return false; else markCol[i][temp] = true; if (markBlock[index][temp]) return false; else markBlock[index][temp] = true; } } } return true; }
对于此题,似乎没法靠优化算法来提升效率,硬要优化的话,可以在位运算上下点功夫。
public boolean isValidSudoku(char[][] board) { int[][] mark = new int[3][9]; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { int index = i/3 * 3 + j/3; if (board[j][i] != '.') { // 1左移?位,数独中最大数为9,2^8大大小于int的max int temp = 1 << (board[j][i] - '1'); // 判断每列是否出现重复用temp,否则|上temp if ((mark[0][j] & temp) > 0) return false; else mark[0][j] |= temp; // 每行 if ((mark[1][i] & temp) > 0) return false; else mark[1][i] |= temp; //每块 if ((mark[2][index] & temp) > 0) return false; else mark[2][index] |= temp; } } } return true; }
进一步简化代码
public boolean isValidSudoku(char[][] board) { int[][] mark = new int[3][9]; for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { int index = i/3 * 3 + j/3; if (board[j][i] != '.') { int temp = 1 << (board[j][i] - '1'); if ((mark[0][j] & temp) > 0 || (mark[1][i] & temp) > 0 || (mark[2][index] & temp) > 0) return false; mark[0][j] |= temp; mark[1][i] |= temp; mark[2][index] |= temp; } } } return true; }