编写一个程序,通过填充空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
提示:
给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
作为一个刚刚起步算法的小弱鸡,稍微记录一下自己对于这题的设计思路吧,没有尝试高大上的办法,我只用了最简单的回溯办法,但是这道题确实加深了我对于回溯的理解。
基本思路是对于一个数独矩阵,设计一个函数helper(board,row,col),对于board[row][col]按进行1-9穷举,并判断isvalid,如果这一位填的数字满足数独矩阵条件,那么就判断helper(board,row,col+1),这里需要思考的是我们希望这个递归函数的意义是什么,我的回答是,我希望输入board,row,col能够判断这之后的整个数独能不能填完,以此为基点思考下去就会遇到一个问题:
最开始我设计的辅助函数是一个无返回的void函数,但是我发现这样子无法知道后一位helper(board,row,col+1)是否能够填完,所以将函数改为bool函数,那么helper(board,row,col)的返回值判断思路如下:
如果board[row][col]填了数字或者能填数字,那么就判断helper(board,row,col+1)是否是true,如果是那么返回true;不是那就说明这一位填的数字不对,那就换一个数字,如果穷举完9个数字都没有返回true,那就说明helper(board,row,col)无法完成数独,所以return false。
回溯的思想则体现在中间每次将choose的数字改回’.'的过程。
class Solution {
public:
void solveSudoku(vector<vector<char>>& board) {
helper(board,0,0);
}
//输入位置返回是否能填成功
bool helper(vector<vector<char>>& board,int row,int col){
if(row==8&&col==9){
//base case
return true;
}
//位置变化
if(col==9){
col=0;
row++;
}
//如果已经有数字了
if(board[row][col]!='.') return helper(board,row,col+1);
//没数字的话穷举此位置1-9
else{
for(int i=1;i<10;i++){
//choose
board[row][col]=(char)(i+'0');
bool flag=isvalid(row,col,board);
if(flag) {
if(helper(board,row,col+1)) return true;
}
//unchoose
board[row][col]='.';
}
//如果9个数都举完,仍然不能返回true,说明是之前选的数字出错了,因此返回false
return false;
}
}
bool isvalid(int row,int col,vector<vector<char>>& board){
int num=board[row][col];
for(int i=0;i<9;i++){
if(i==col) continue;
if(board[row][i]==num) return false;
}
for(int i=0;i<9;i++){
if(i==row) continue;
if(board[i][col]==num) return false;
}
int a=row/3;
int b=col/3;
for(int i=a*3;i<(a+1)*3;i++){
for(int j=b*3;j<(b+1)*3;j++){
if(row==i&&col==j) continue;
if(board[i][j]==num) return false;
}
}
return true;
}
};