37. 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()进行判断),再向后递归,如果递归能够解出数独,说明当前填入的数字是正确的,问题已解决直接返回即可;否则将填入的数字恢复为空格,等待填入下一个数字。不断的“试错”后就能得到正确答案。


代码实现 - 无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 (最先想出的杂技解法hhh)

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);
                }
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值