题目

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

  1. Each of the digits 1-9 must occur exactly once in each row.
  2. Each of the digits 1-9 must occur exactly once in each column.
  3. Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

Empty cells are indicated by the character '.'.
image.png
A sudoku puzzle…
image.png
…and its solution numbers marked in red.

Note:

  • The given board contain only digits 1-9 and the character '.'.
  • You may assume that the given Sudoku puzzle will have a single unique solution.
  • The given board size is always 9x9.

题意

完成数独。

思路

典型的“试错型”问题,使用回溯法:遍历每个空格,每次先插入一个有效的数字(有效性通过isValid()进行判断),再向后递归,如果递归能够解出数独,说明当前填入的数字是正确的,问题已解决直接返回即可;否则将填入的数字恢复为空格,等待填入下一个数字。不断的“试错”后就能得到正确答案。


代码实现

Java

无hash

  1. class Solution {
  2. public void solveSudoku(char[][] board) {
  3. solve(board, 0, 0);
  4. }
  5. private boolean solve(char[][] board, int i, int j) {
  6. // 每一格都填上了数字,说明已经解出数独
  7. if (i == board.length) {
  8. return true;
  9. }
  10. int nextI = j == board.length - 1 ? i + 1 : i;
  11. int nextJ = j == board.length - 1 ? 0 : j + 1;
  12. // 已有数字则不需要处理,直接向后递归
  13. if (board[i][j] != '.') {
  14. return solve(board, nextI, nextJ);
  15. }
  16. for (int k = 1; k <= 9; k++) {
  17. char c = (char) ('0' + k);
  18. if (isValid(board, i, j, c)) {
  19. board[i][j] = c;
  20. boolean solved = solve(board, nextI, nextJ);
  21. // 如果递归能解出数独,说明当前填入的数字是正确的
  22. // 否则恢复原状,以免影响下次循环有效性判断的正确性
  23. if (solved) {
  24. return true;
  25. } else {
  26. board[i][j] = '.';
  27. }
  28. }
  29. }
  30. return false;
  31. }
  32. private boolean isValid(char[][] board, int i, int j, char c) {
  33. for (int k = 0; k < 9; k++) {
  34. // 注意要排除自身
  35. if (k != j && c == board[i][k] || k != i && c == board[k][j]) {
  36. return false;
  37. }
  38. }
  39. // x、y的初始值为对应九宫格左上顶点的坐标
  40. for (int x = 3 * (i / 3); x < 3 * (i / 3) + 3; x++) {
  41. for (int y = 3 * (j / 3); y < 3 * (j / 3) + 3; y++) {
  42. // 注意要排除自身
  43. if (c == board[x][y] && (x != i || y != j)) {
  44. return false;
  45. }
  46. }
  47. }
  48. return true;
  49. }
  50. }

hash

  1. class Solution {
  2. List<Set<Character>> row = new ArrayList<>();
  3. List<Set<Character>> col = new ArrayList<>();
  4. List<Set<Character>> nine = new ArrayList<>();
  5. public void solveSudoku(char[][] board) {
  6. initialize(board);
  7. solve(board, 0, 0);
  8. }
  9. private boolean solve(char[][] board, int i, int j) {
  10. if (i == board.length) {
  11. return true;
  12. }
  13. int nextI = j == board.length - 1 ? i + 1 : i;
  14. int nextJ = j == board.length - 1 ? 0 : j + 1;
  15. if (board[i][j] != '.') {
  16. return solve(board, nextI, nextJ);
  17. }
  18. for (int k = 1; k <= 9; k++) {
  19. char c = (char) ('0' + k);
  20. if (!row.get(i).contains(c)
  21. && !col.get(j).contains(c)
  22. && !nine.get(3 * (i / 3) + j / 3).contains(c)) {
  23. board[i][j] = c;
  24. row.get(i).add(c);
  25. col.get(j).add(c);
  26. nine.get(3 * (i / 3) + j / 3).add(c);
  27. boolean flag = solve(board, nextI, nextJ);
  28. if (flag) {
  29. return true;
  30. } else {
  31. board[i][j] = '.';
  32. row.get(i).remove(c);
  33. col.get(j).remove(c);
  34. nine.get(3 * (i / 3) + j / 3).remove(c);
  35. }
  36. }
  37. }
  38. return false;
  39. }
  40. private void initialize(char[][] board) {
  41. for (int i = 0; i < 9; i++) {
  42. row.add(new HashSet<>());
  43. col.add(new HashSet<>());
  44. nine.add(new HashSet<>());
  45. }
  46. for (int i = 0; i < 9; i++) {
  47. for (int j = 0; j < 9; j++) {
  48. char c = board[i][j];
  49. if (c != '.') {
  50. row.get(i).add(c);
  51. col.get(j).add(c);
  52. nine.get(3 * (i / 3) + j / 3).add(c);
  53. }
  54. }
  55. }
  56. }
  57. }

JavaScript

无Hash

  1. /**
  2. * @param {character[][]} board
  3. * @return {void} Do not return anything, modify board in-place instead.
  4. */
  5. var solveSudoku = function (board) {
  6. dfs(board, 0, 0)
  7. }
  8. let dfs = function (board, row, col) {
  9. if (row === board.length) {
  10. return true
  11. }
  12. let nextRow = col === 8 ? row + 1 : row
  13. let nextCol = col === 8 ? 0 : col + 1
  14. if (board[row][col] !== '.') {
  15. return dfs(board, nextRow, nextCol)
  16. }
  17. for (let i = 1; i <= 9; i++) {
  18. let c = '' + i
  19. if (isValid(board, row, col, c)) {
  20. board[row][col] = c
  21. if (dfs(board, nextRow, nextCol)) {
  22. return true
  23. }
  24. board[row][col] = '.'
  25. }
  26. }
  27. return false
  28. }
  29. let isValid = function (board, row, col, c) {
  30. for (let i = 0; i < 9; i++) {
  31. if ((row !== i && board[i][col] === c) || (col !== i && board[row][i] === c)) {
  32. return false
  33. }
  34. }
  35. let topLeftX = Math.trunc(row / 3) * 3
  36. let topLeftY = Math.trunc(col / 3) * 3
  37. for (let i = topLeftX; i < topLeftX + 3; i++) {
  38. for (let j = topLeftY; j < topLeftY + 3; j++) {
  39. if (c === board[i][j] && (i !== row || j !== col)) {
  40. return false
  41. }
  42. }
  43. }
  44. return true
  45. }

Hash

/**
 * @param {character[][]} board
 * @return {void} Do not return anything, modify board in-place instead.
 */
var solveSudoku = function (board) {
  let rows = new Array(9).fill(0).map(v => new Set())
  let cols = new Array(9).fill(0).map(v => new Set())
  let boxes = new Array(9).fill(0).map(v => new Set())

  for (let i = 0; i < 9; i++) {
    for (let j = 0; j < 9; j++) {
      let c = board[i][j]
      rows[i].add(c)
      cols[j].add(c)
      boxes[Math.trunc(i / 3) * 3 + Math.trunc(j / 3)].add(c)
    }
  }

  dfs(board, 0, 0, rows, cols, boxes)
}

let dfs = function (board, i, j, rows, cols, boxes) {
  if (i === 9) {
    return true
  }

  let nextI = j === 8 ? i + 1 : i
  let nextJ = j === 8 ? 0 : j + 1

  if (board[i][j] !== '.') {
    return dfs(board, nextI, nextJ, rows, cols, boxes)
  }

  let boxIndex = Math.trunc(i / 3) * 3 + Math.trunc(j / 3)
  for (let num = 1; num <= 9; num++) {
    let c = num + ''
    if (rows[i].has(c) || cols[j].has(c) || boxes[boxIndex].has(c)) {
      continue
    }
    board[i][j] = c
    rows[i].add(c)
    cols[j].add(c)
    boxes[boxIndex].add(c)
    if (dfs(board, nextI, nextJ, rows, cols, boxes)) {
      return true
    }
    board[i][j] = '.'
    rows[i].delete(c)
    cols[j].delete(c)
    boxes[boxIndex].delete(c)
  }

  return false
}