37. Sudoku Solver

37. Sudoku Solver

Write a program to solve a Sudoku puzzle by filling the empty cells.

A sudoku solution must satisfy all of the following rules:

Each of the digits 1-9 must occur exactly once in each row.
Each of the digits 1-9 must occur exactly once in each column.
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.

方法1:

grandyang:http://www.cnblogs.com/grandyang/p/4421852.html
思路:

使用backtracking来找valid 解,和N-Queens这道题很像。首先上来判断一些不合法的board,可以加速一点。如果合法开始dfs。这里helper为什么要用bool作为return呢?主要是我们要在遇到合法解的时候马上作出判断,比如这道题里是马上返回当前棋盘(assuming 唯一解存在)。确定了返回值之后就可以开始写dfs。首先写base case:当棋盘全部填满的时候且过程中没有发生不合法填数,可以直接返回true,以判断发现合法解,上层函数需要想办法返回当前棋盘。如果按照横向优先一步一步填数的话,这个临界条件出现在x == 9,行坐标超出边界时。否则如果当前位置为空“.”, 尝试在当前位置放入1-9其中一个数字。如果当前位置不为空,直接跳过这个位置,继续dfs下一位。尝试放入数字时按照以下做法:每次propose一个数字,都要把棋盘传入isValid这个function当中check一下是否合法,如果立刻不合法,这个propose的数字不可能产生合法解,循环至下一个数字。如果该位置放入的是合法解,这个递归会一直延续,直到最后一层返回true。这说明:1. 递归调用放入proposed数字的棋盘,判断其返回值,如果true,可以直接结束dfs,把棋盘返回主函数;2. 当所有数字都尝试一遍却没有发现合法解的时候,需要返回false。如果下一层函数无法放入任何数字,说明当前这个位置的数字也放错了,尝试下一个propose。返回false的时候一定要移除放入的值:要为之后的合法解让路。

易错点

  1. 用这种横向优先填入的时候,需要手动换行。并且换行要在判断base之前完成。
  2. 判断当前点是否需要放入propose:“.”
  3. 当前dfs没有找到合法解要恢复“.”

Complexity

Time complexity : O(9^m),
Space complexity : O(m), m是’.'的数目。

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        if (board.empty() || board.size() != 9 || board[0].size() != 9) return;
        solveHelper(board, 0, 0) ;
        return;
    }
    
    
    bool solveHelper(vector<vector<char>> & board, int x, int y){
        if (y >= 9) {
            x ++;
            y = 0;
        }
        if (x == 9) return true;
      
        if (board[x][y] == '.'){
            for (int k = 1; k <= 9; k++){
                board[x][y] = char(k + '0');
                if (isValid(board, x, y)){
                    if  (solveHelper(board, x, y + 1))
                        return true;
                }
                board[x][y] = '.';
            }
            // board[x][y] = '.';
        }
        else {
            return solveHelper(board, x, y + 1);
        }
        return false;
        
    }
    
    // i , j 是本次新放进去的数字位置,只需要检查它所在的行列小九宫格内部有没有conflict
    bool isValid(vector<vector<char>> & board, int x, int y){
        for (int i = 0; i < 9; i ++){
            // row
            if (i != x && board[i][y] == board[x][y]) return false;
            // col
            if (i != y && board[x][i] == board[x][y]) return false;
            // cube
            int r = (x / 3) * 3 + (i / 3);
            int c = (y / 3) * 3 + (i % 3);
            if ((r != x || c != y) && board[r][c] == board[x][y]) return false;
        }
        return true;
    }
};

下面的解法来自YRB: https://www.cnblogs.com/yrbbest/p/4436325.html。一种非常整洁的写法。

public class Solution {
    public void solveSudoku(char[][] board) {
        canSolveSudoku(board);
    }
    
    private boolean canSolveSudoku(char[][] board) {
        if (board == null || board.length == 0) {
            return false;
        }
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board.length; j++) {
                if (board[i][j] == '.') {
                    for (char c = '1'; c <= '9'; c++) {
                    if (isCurrentBoardValid(board, i, j, c)) {
                            board[i][j] = c;
                            if (canSolveSudoku(board)) {
                                return true;
                            } else {
                                board[i][j] = '.';       // backtracking
                            }
                        }
                    }
                    return false;    
                }
            }
        }
        return true;
    }
    
    private boolean isCurrentBoardValid(char[][] board, int row, int col, char c) {
        for (int i = 0; i < board.length; i++) {
            if (board[i][col] == c) {
                return false;
            }
        }
        
        for (int j = 0; j < board[0].length; j++) {
            if (board[row][j] == c) {
                return false;
            }
        }
        
        for (int i = row / 3 * 3; i < row / 3 * 3 + 3; i++) {
            for (int j = col / 3 * 3; j < col /3 * 3 + 3; j++) {
                if (board[i][j] == c) {
                    return false;
                }
            }
        }
        return true;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值