37. 解数独

37. 解数独

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

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

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例:

输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字或者 '.'
  • 题目数据 保证 输入数独仅有一个解
class Solution {
    public void solveSudoku(char[][] board) {
        // 九宫格的九个基点
        Map<Integer, int[]> map = new HashMap<Integer, int[]>(){{
            put(0, new int[]{0,0});
            put(1, new int[]{0,3});
            put(2, new int[]{0,6});
            put(3, new int[]{3,0});
            put(4, new int[]{3,3});
            put(5, new int[]{3,3});
            put(6, new int[]{6,0});
            put(7, new int[]{6,3});
            put(8, new int[]{6,6});
        }};
        char[][] result = new char[9][9];
        huisu(board, 0, 0, map, result);
        // 记录下答案,这是正确的
        for(int m = 0; m < 9; ++m){
            for(int n = 0; n < 9; ++n) {
                board[m][n] = result[m][n];
            }
        }
    }

    public void huisu(char[][] board, int x, int y, Map<Integer, int[]> map, char[][] result){
        // 最后一步
        if(x == 8 && y == 8){
            if(board[y][x] == '.'){
                // 最后填入一个数字,不需要在往深处走
                for(int i = 1; i <= 9; ++i){
                    // 先改为数字
                    board[y][x] = (char)(i + 48);
                    if(isValidSudoku(board, x, y, map)){
                        // 记录下答案,这是正确的
                        for(int m = 0; m < 9; ++m){
                            for(int n = 0; n < 9; ++n){
                                result[m][n] = board[m][n];
                            }
                        }
                        return;
                    }
                    // 回溯
                    board[y][x] = '.';
                }
            } else {
                // 记录下答案,这是正确的
                for(int m = 0; m < 9; ++m){
                    for(int n = 0; n < 9; ++n){
                        result[m][n] = board[m][n];
                    }
                }
                return;
            }
        }
        // 说明是要填入的
        if(board[y][x] == '.'){
            for(int i = 1; i <= 9; ++i){
                // 先改为数字
                board[y][x] = (char)(i + 48);
                // 如果是正确的,继续填入下一个
                // 如果不正确的就直接不用进行下一步了,并且会一直回溯到上一个正确的点,输入下一个数字。
                if(isValidSudoku(board, x, y, map)){
                    huisu(board, (x + 1) % 9, y + ((x + 1) / 9), map, result);
                }
                // 回溯
                board[y][x] = '.';
            }
        } else {
            huisu(board, (x + 1) % 9, y + ((x + 1) / 9), map, result);
        }
    }

    public boolean isValidSudoku(char[][] board, int x, int y, Map<Integer, int[]> map) {
        // 先根据传入的xy去判断处于第几个九宫格,获取对应的基点坐标
        int[] ints = map.get(x / 3 + y / 3 * 3);
        // result:比对数据的数组
        int[] result = new int[9];
        // temp:暂时存放数据。
        int temp;
        // 能走出验证横线的循环说明横线没有问题,接下来刷新result数组,重新验证竖线
        for(int m = 0; m < 9; m++){
            if(board[y][m] == '.'){
                continue;
            }
            temp = board[y][m] - '1';
            // 说明之前已经加过了,即出现了重复
            if(result[temp] > 0){
                return false;
            }
            result[temp]++;
        }
        for(int i = 0; i < 9; ++i){
            result[i] = 0;
        }
        // 能走出竖线的循环说明竖线没有问题,接下来刷新result数组,重新验证九宫格
        for(int m = 0; m < 9; ++m){
            if(board[m][x] == '.'){
                continue;
            }
            temp = board[m][x] - '1';
            if(result[temp] > 0){
                return false;
            }
            result[temp]++;
        }
        for(int i = 0; i < 9; ++i){
            result[i] = 0;
        }
        y = ints[0];
        x = ints[1];
        // 能走出九宫格循环说明九宫格没有问题
        for(int m = 0; m < 3; ++m){
            for(int n = 0; n < 3; ++n){
                if(board[y + m][x + n] == '.'){
                    continue;
                }
                temp = board[y + m][x + n] - '1';
                if(result[temp] > 0){
                    return false;
                }
                result[temp]++;
            }
        }
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值