Backtracking_37. 解数独

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。

 

Note:

  • 给定的数独序列只包含数字 1-9 和字符 '.' 。
  • 你可以假设给定的数独只有唯一解。
  • 给定数独永远是 9x9 形式的。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver


思路:

这是一道困难题,说实话做了很久0.0

要用到回溯的算法,总的思想,可以暴力解决,每个格子都从1-9来填进去看看行不行,如果不行就换,行就往下不行再退回来

当然我们不这么做,要排除掉之前就已经不能填的格子比如在【0,2】这个格子,在第0行上要排除5,3,7这三个数,横向有了再填肯定不行

再排除纵向,有个8,再排除box范围,5,3,6,9所以最后能填的只有1,2,4三个数先填上1,然后看后一个格子,这个时候横向要多排除一个1,其他老样子排除

再往后还是这样,一直到最后一个,看看行不行,不行了就回溯,假设第一个填1一直都不行,那就换2,还不行再换4......

最后第一行找完了,那就跳到第二行,依次类推下去

class Solution {
    //定义三个数组,分别用于记录行使用情况,列使用情况和九个小格子使用情况
    int [][] row;
    int [][] col;
    int [][] block;
    //定义N存储给定数组的长度
    int N;
    boolean solve = false;
    //定义第一个方法,主要用于初始化数据
    public void solveSudoku(char [][] board){
        N = board.length;
        row = new int[N][N];
        col = new int[N][N];
        block = new int[N][N];
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (board[i][j] != '.'){
                    //将写在board上的具体值转换为它应该加入的坐标值
                    int t = board[i][j] - '0' - 1;
                    writeDown(board,i,j,t);
                }
            }
        }
        dfs(board,0,0);
    }

    private void dfs(char[][] board, int i, int j){
        int box_number = (i / 3) * 3 + j / 3;
        //如果当前遍历到的是.那就说明要写入了,否则就走下一步
        if (board[i][j] == '.'){
            //可能填入的值有1-9,注意此处的循环是0-8
            for (int k = 0; k < N; k++) {
                //判断当前的数字是否已经用过,除非行、列、格子全部都为0否则就是重复了
                if (row[i][k] + col[j][k] + block[box_number][k] != 0){
                    continue;
                }
                writeDown(board,i,j,k);
                next(board,i,j);
                if (!solve)
                clear(board,i,j,k);
            }
        }else {
            next(board,i ,j);
        }
    }

    //走下一步
    private void next(char[][] board,int i,int j){
        //说明已经到了终点
       if (i == N - 1 && j == N - 1) solve = true;
        else {
            if (j == N - 1){
                dfs(board,i + 1,0);
            }else {
                dfs(board,i,j + 1);
            }
        }
    }

    //定义一个写入的函数
    private void writeDown(char[][] board, int i, int j, int t) {
        //将该位置有数字的坐标记录到对应的记录表上去,分别是对应行,对应列,对应格子
        //将坐标转为表格号
        int box_number = (i / 3) * 3 + j / 3;
        //用1表示对应位置有数字,也就是写过了,后面遍历的时候可以跳过这个数字
        row[i][t] = 1;
        col[j][t] = 1;
        block[box_number][t] = 1;
        //之前把值转换为了坐标值,现在要给他赋回去
        board[i][j] =(char) (t + '0' + 1);
    }

    //对应的还要有一个清除的回溯用的方法
    private void clear(char[][] board, int i, int j, int t) {
        //将该位置有数字的坐标记录到对应的记录表上去,分别是对应行,对应列,对应格子
        //将坐标转为表格号
        int box_number = (i / 3) * 3 + j / 3;
        //用1表示对应位置有数字,也就是写过了,后面遍历的时候可以跳过这个数字
        row[i][t] = 0;
        col[j][t] = 0;
        block[box_number][t] = 0;
        board[i][j] = '.';
    }
}
原文地址:https://www.cnblogs.com/zzxisgod/p/13380078.html