LeetCode题目:解数独

题目描述:

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

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

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
在这里插入图片描述
一个数独。
在这里插入图片描述
答案被标成红色。

Note:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

这道题不难需要试不同的情况(就是不同的数字),数字对了就继续往下一步,数字不对的要返回上一步,或者返回上几步,所以不难想到这道题是用回溯方法解,但是没有想到答案想的这么巧妙,这么全面。
答案的思路是这样的:
一行试完了完下一行(如果col == 9,则backTrace(board, row + 1, 0)),当所有的行都试完了,返回true。每一行里面,如果当前这格里面的内容不是 ‘.’ 的,就直接下一格,是 ‘.’ 的说明就是需要填数字的,开始尝试1-9往里放,并判断放入的这个数字是不是有效的,无效的就直接尝试下一个数字,如果所有的数字都尝试完了还没有找到一个有效的,重新放回 ‘.’ ,并返回上一级。其中在判断填入的数字是否有效的时候,用到一个很巧妙的方法表示当前数字所在的九宫格:
board[(i/3)*3 + k/3][(j/3)*3 + k % 3]
i就是当前数字所在9X9矩阵里面的行数,k就是当前九宫格的第k个数字,j是当前数字所在9X9矩阵里面的列数。这样就能把当前数字所在的九宫格里面的元素都表示出来。

代码(Java):

public class Solution {
	public void solution(char[][] board) {
        // 非法数独
        if (board == null || board.length != 9 || board[0] == null || board[0].length != 9)
            return;
        // 回溯法解决
        backTrace(board, 0, 0);
    }

    private boolean backTrace(char[][] board, int row, int col){
        int n = board.length; // 9
        // 当前行已全部试探过,换到下一行第一个位置
        if (col == 9)
            return backTrace(board, row + 1, 0);
        // 满足结束条件,全部行全部位置都已试探过
        if (row == n)
            // 最后一行最后一个位置[8][8]试探过后会试探[8][9],会执行[9][0],返回
            return true;
        // 这个位置数字已给出,不需要试探,直接试探下一个位置
        if (board[row][col] != '.')
            return backTrace(board, row, col + 1);
        // 遍历可选择列表(各选择之间并列)
        for (char c = '1'; c <= '9'; c++){
            // 排除不合法的选择
            if (!isValid(board, row, col, c))
                continue;
            // 做选择
            board[row][col] = c;
            // 进行下一步试探,发现当前选择能成功进行下去,就继续往下
            if (backTrace(board, row, col + 1))
                return true;
            // 撤销本次选择,并列进行下一次选择的试探
            board[row][col] = '.';
        }
        // 这个位置把1-9都试过了,都无法继续下去,说明上一次的选择失败,需要回溯
        return false;
    }

    /**
     * 判断 board[row][col]位置放入字符 ch,是否合理
     * 也就判断这个字符有没有在 同一行,同一列,同一个子数独中出现过
     * 行列比较容易,就是一个for循环
     * 而对于 给定的 board[i][j],它所在的子数独的索引是 (i / 3) * 3 + j / 3
     * 要扫描这个子数独中的全部9个元素,for循环可以这样写
     * boardIndex = (i / 3) * 3 + j / 3
     * for(int k = 0; k < 9; k++){
     * board[(i/3)*3 + k/3][(j/3)*3 + k % 3]
     * }
     * 因为 i和j是确定的,所以 i / 3 * 3可以确定他所在的子数独在第一个三行,还是第二个三行,还是第三个三行
     * j / 3 * 3可以确定它所在的子数独是前三列还是中散列还是后三列,
     * 相当于这两个只是确定了这个【子数独的左上角坐标】,而需要借助 k 完全对这个9个位置的扫描
     *
     * @param board
     * @param row
     * @param col
     * @param ch
     * @return
     */
    private boolean isValid(char[][] board, int row, int col, char ch) {
        // 三个方向,任一方向重复,ch就不能放在这个位置
        for (int k = 0; k < 9; k++) {
            // 同一行九个位置已出现 ch
            if (board[row][k] == ch) return false;
            // 同一列九个位置中已出现 ch
            if (board[k][col] == ch) return false;
            // 同一个子数独九个位置中已出现 ch
            if (board[(row / 3) * 3 + k / 3][(col / 3) * 3 + k % 3] == ch) return false;
        }
        return true;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值