zoukankan      html  css  js  c++  java
  • 0037. Sudoku Solver (H)

    Sudoku Solver (H)

    题目

    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 '.'.

    A sudoku puzzle...

    ...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

    class Solution {
        public void solveSudoku(char[][] board) {
            solve(board, 0, 0);
        }
    
        private boolean solve(char[][] board, int i, int j) {
       		// 每一格都填上了数字,说明已经解出数独
            if (i == board.length) {
                return true;
            }
    
            int nextI = j == board.length - 1 ? i + 1 : i;
            int nextJ = j == board.length - 1 ? 0 : j + 1;
    		
            // 已有数字则不需要处理,直接向后递归
            if (board[i][j] != '.') {
                return solve(board, nextI, nextJ);
            }
    
            for (int k = 1; k <= 9; k++) {
                char c = (char) ('0' + k);
                if (isValid(board, i, j, c)) {
                    board[i][j] = c;
                    boolean solved = solve(board, nextI, nextJ);
                    // 如果递归能解出数独,说明当前填入的数字是正确的
                    // 否则恢复原状,以免影响下次循环有效性判断的正确性
                    if (solved) {
                        return true;
                    } else {
                        board[i][j] = '.';
                    }
                }
            }
    
            return false;
        }
    
        private boolean isValid(char[][] board, int i, int j, char c) {
            for (int k = 0; k < 9; k++) {
                // 注意要排除自身
                if (k != j && c == board[i][k] || k != i && c == board[k][j]) {
                    return false;
                }
            }
    		
            // x、y的初始值为对应九宫格左上顶点的坐标
            for (int x = 3 * (i / 3); x < 3 * (i / 3) + 3; x++) {
                for (int y = 3 * (j / 3); y < 3 * (j / 3) + 3; y++) {
                    // 注意要排除自身
                    if (c == board[x][y] && (x != i || y != j)) {
                        return false;
                    }
                }
            }
    
            return true;
        }
    }
    

    hash

    class Solution {
        List<Set<Character>> row = new ArrayList<>();
        List<Set<Character>> col = new ArrayList<>();
        List<Set<Character>> nine = new ArrayList<>();
    
        public void solveSudoku(char[][] board) {
            initialize(board);
            solve(board, 0, 0);
        }
    
        private boolean solve(char[][] board, int i, int j) {
            if (i == board.length) {
                return true;
            }
            
            int nextI = j == board.length - 1 ? i + 1 : i;
            int nextJ = j == board.length - 1 ? 0 : j + 1;
    
            if (board[i][j] != '.') {
                return solve(board, nextI, nextJ);
            }
    
            for (int k = 1; k <= 9; k++) {
                char c = (char) ('0' + k);
                if (!row.get(i).contains(c)
                        && !col.get(j).contains(c)
                        && !nine.get(3 * (i / 3) + j / 3).contains(c)) {
                    board[i][j] = c;
                    row.get(i).add(c);
                    col.get(j).add(c);
                    nine.get(3 * (i / 3) + j / 3).add(c);
                    boolean flag = solve(board, nextI, nextJ);
                    if (flag) {
                        return true;
                    } else {
                        board[i][j] = '.';
                        row.get(i).remove(c);
                        col.get(j).remove(c);
                        nine.get(3 * (i / 3) + j / 3).remove(c);
                    }
                }
            }
            
            return false;
        }
    
        private void initialize(char[][] board) {
            for (int i = 0; i < 9; i++) {
                row.add(new HashSet<>());
                col.add(new HashSet<>());
                nine.add(new HashSet<>());
            }
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    char c = board[i][j];
                    if (c != '.') {
                        row.get(i).add(c);
                        col.get(j).add(c);
                        nine.get(3 * (i / 3) + j / 3).add(c);
                    }
                }
            }
        }
    }
    

    JavaScript

    无Hash

    /**
     * @param {character[][]} board
     * @return {void} Do not return anything, modify board in-place instead.
     */
    var solveSudoku = function (board) {
      dfs(board, 0, 0)
    }
    
    let dfs = function (board, row, col) {
      if (row === board.length) {
        return true
      }
    
      let nextRow = col === 8 ? row + 1 : row
      let nextCol = col === 8 ? 0 : col + 1
    
      if (board[row][col] !== '.') {
        return dfs(board, nextRow, nextCol)
      }
    
      for (let i = 1; i <= 9; i++) {
        let c = '' + i
        if (isValid(board, row, col, c)) {
          board[row][col] = c
          if (dfs(board, nextRow, nextCol)) {
            return true
          }
          board[row][col] = '.'
        }
      }
    
      return false
    }
    
    let isValid = function (board, row, col, c) {
      for (let i = 0; i < 9; i++) {
        if ((row !== i && board[i][col] === c) || (col !== i && board[row][i] === c)) {
          return false
        }
      }
    
      let topLeftX = Math.trunc(row / 3) * 3
      let topLeftY = Math.trunc(col / 3) * 3
    
      for (let i = topLeftX; i < topLeftX + 3; i++) {
        for (let j = topLeftY; j < topLeftY + 3; j++) {
          if (c === board[i][j] && (i !== row || j !== col)) {
            return false
          }
        }
      }
    
      return true
    }
    

    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
    }
    ```![](https://img2020.cnblogs.com/blog/2064127/202006/2064127-20200626034712017-1380013500.png)
  • 相关阅读:
    WPF进阶技巧和实战03-控件(3-文本控件及列表控件)
    WPF进阶技巧和实战08-依赖属性与绑定03
    WPF进阶技巧和实战08-依赖属性与绑定02
    WPF进阶技巧和实战08-依赖属性与绑定01
    WPF进阶技巧和实战07--自定义元素02
    WPF进阶技巧和实战07--自定义元素01
    Codeforces Round #730 Div. 2
    Codeforces Round #701 Div. 2
    Codeforces Round #700 Div. 2
    Codeforces Round #699 Div. 2
  • 原文地址:https://www.cnblogs.com/mapoos/p/13193719.html
Copyright © 2011-2022 走看看