LeetCode-37.解数独(考察点:回溯算法+剪枝)

117 篇文章 0 订阅

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

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

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

一个数独。

答案被标成红色。

Note:

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

解题思路 :

总共有81个元素,按行顺序处理,遇到空白格,则依次尝试填入1-9的数字,判断填入之后是否为有效的数独(可调用LeetCode-36.有效的数独的的函数),若为有效的数独,则继续处理后续的空白格(处理方式仍然是依次尝试填入1-9的数字,然后判断数独是否有效),当遇到无效的数独时,则回溯,所有元素处理完,则数独解完。

这种解法有两个非常耗时的地方:

  1. 遇到空白格,依次尝试1-9的数字,会做很多重复运算,比如第一行已经有5、 3,遇到空白格其实就不必尝试再填入5、 3
  2. 每次在空白格填入一个数字之后,都需要判断一遍当前数独是否有效

优化:用三个boolean型的二维数组l[][]、c[][]、s[][],

  • l[i][j]表示第i行是否已经出现j+1这个数字
  • c[i][j]表示第i列是否已经出现j+1这个数字
  • s[i][j]表示第i个3x3九宫格是否已经出现j+1这个数字

并且每次遇到空白格时,依次尝试1-9的数字前,判断空白格所属的行、列以及3x3的九宫格是否已经出现该数字,若出现过,则直接跳过(剪枝),便可保证每次填入数字之后的数独都是有效的,无需再判断。

代码如下:

class Solution {
    public void solveSudoku(char[][] board) {
        boolean[][] l = new boolean[9][9];//记录第i行是否已包含j+1字符
        boolean[][] c = new boolean[9][9];//记录第i列是否已包含j+1字符
        boolean[][] s = new boolean[9][9];//记录第i个九宫格是否已包含j+1字符
        for(int line = 0; line < 9; line++){
            for(int col = 0; col < 9; col++){
                if('.' == board[line][col])
                    continue;

                l[line][board[line][col] - '1'] = true;
                c[col][board[line][col] - '1'] = true;
                s[line/3*3 + col/3][board[line][col] - '1'] = true;
            }
        }

        solve(board, 0, l, c, s);
    }

    private boolean solve(char[][] board, int position, boolean[][] l, boolean[][] c, boolean[][] s){
        int index = position;
        for(; index < 81 && '.' != board[index/9][index%9]; index++);

        if(index < 81){
            int line = index/9;
            int col = index%9;
            for(int i = 1; i < 10; i++){
                if(l[line][i - 1] || c[col][i - 1] || s[line/3*3 + col/3][i - 1])
                    continue;

                board[line][col] = (char)('0' + i);
                l[line][i - 1] = true;
                c[col][i - 1] = true;
                s[line/3*3 + col/3][i - 1] = true;

                if(solve(board, index+1, l, c, s))
                    return true;

                board[line][col] = '.';
                l[line][i - 1] = false;
                c[col][i - 1] = false;
                s[line/3*3 + col/3][i - 1] = false;
            }
            return false;
        }
        return true;
    }
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值