回溯算法最佳实践:解数独 :: labuladong的算法小抄 #1024
Replies: 15 comments 12 replies
-
auto.js不错 |
Beta Was this translation helpful? Give feedback.
-
这道题我一开始写成了如下的回溯,发现最后返回的board是和输入的一样。我理解是回溯过程中没有及时退出,将原本”有解的board“又一步步还原成了”原始board“,各位大佬看下我的分析对吗。(去掉注释以后的代码可以正确运行) void dfs(vector<vector<char>>& board, int x, int y) {
if (x == 9) {
// ans = board; //这里需要用ans来保存中间成功的结果,否则会在dfs回溯中被清成'.'
//flag = true;
return;
}
if (y == 9) {
dfs(board, x + 1, 0);
return;
}
if (board[x][y] != '.') {
dfs(board, x, y + 1);
return;
}
for (char i = '1'; i <= '9'; i++) {
if (valid(board, x, y, i)) {
board[x][y] = i;
dfs(board, x, y + 1); //要么写成if (dfs(...)) return true;的形式,要么用ans来保存成功时board的状态。否则会在回溯过程,board变为原来的样子
//if (flag) return;
board[x][y] = '.';
}
}
return;
} |
Beta Was this translation helpful? Give feedback.
-
@typedefstruct32 你的想法是对的,一般我在写的时候都是在类里写一个变量,然后算出一个结果就把他加进去,如果只是单纯回溯的话一定会变回最开始的样子,因为每次修改后都会把它改回去 |
Beta Was this translation helpful? Give feedback.
-
请问周围一圈的位置判断公式怎么得出的 |
Beta Was this translation helpful? Give feedback.
-
Subbox 这段代码
如何找【4,6】的subbox |
Beta Was this translation helpful? Give feedback.
-
class Solution {
// 回溯法
public void solveSudoku(char[][] board) {
backTraverse(board,0,0);
}
public boolean backTraverse(char[][] board,int i,int j){
int m = board.length;
int n = board[0].length;
// 递归完一列,则换下一行
if(j == n){
return backTraverse(board,i + 1,0);
}
// 递归完所有行
if(i == m){
return true;
}
// 当前位置已经有数字则填下一个位置
if(board[i][j] != '.'){
return backTraverse(board,i,j + 1);
}
for(char c = '1';c <= '9';c++){
if(!isValid(board,i,j,c)){
continue;
}
// 选择
board[i][j] = c;
if(backTraverse(board,i,j + 1)){
return true;
}
// 撤销选择
board[i][j] = '.';
}
return false;
}
// 判断board[i][j] 位置是否可以放置 c
public boolean isValid(char[][] board,int i,int j,char c){
int m = board.length;
int n = board[0].length;
for(int k = 0;k < n;k++){
if(board[i][k] == c)
return false;
}
for(int k = 0;k < m;k++){
if(board[k][j] == c)
return false;
}
for(int k = i / 3 * 3;k <= i / 3 * 3 + 2;k++){
for(int l = j / 3 * 3;l <= j / 3 * 3 + 2;l++){
if(board[k][l] == c){
return false;
}
}
}
return true;
}
} |
Beta Was this translation helpful? Give feedback.
-
这个脚本是怎么启动运行的呢 |
Beta Was this translation helpful? Give feedback.
-
判断 3 x 3 方框的逻辑也太抽象了。。 |
Beta Was this translation helpful? Give feedback.
-
感觉如果遵循 |
Beta Was this translation helpful? Give feedback.
-
东哥您好,请问为什么这两个判断的后面要加return,期待您的解答,谢谢🙏
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
不写成boolean返回值也可以,就是需要一个boolean variable。
public void solveSudoku(char[][] board) {
backtrack(board,0,0);
}
boolean solved = false;
public void backtrack(char[][] board, int i, int j){
int row = 9, col = 9;
if(j==col){
backtrack(board,i+1,0);
return;
}
if(i==row){
//达到最后一行,说明已经解出答案
solved=true;
return;
}
if(board[i][j]!='.'){
backtrack(board,i,j+1);
return;
}
for(char c = '1';c<='9';c++){
if(!isValid(board,i,j,c)) continue;
board[i][j]=c;
backtrack(board,i,j+1);
// 如果这个问题已经被解决了,我们不应该把board还原成`.`
if(solved) break;
board[i][j]='.';
}
}
public boolean isValid(char[][] board, int i ,int j, int target){
for(int row = 0;row<board.length;row++){
if(board[row][j]==target) return false;
}
for(int col = 0;col<board[0].length;col++){
if(board[i][col]==target) return false;
}
int startRow = (i/3)*3;
int startCol = (j/3)*3;
for(int row = startRow;row<startRow+3;row++){
for(int col = startCol;col<startCol+3;col++){
if(board[row][col]==target) return false;
}
}
return true;
} |
Beta Was this translation helpful? Give feedback.
-
public void solveSudoku(char[][] board) {
backtrack(board, 0, 0);
}
boolean backtrack(char[][] board, int row, int col) {
int size = 9;
if (col == size) {
// 穷举到最后一列的话就换到下一行重新开始。
return backtrack(board, row + 1, 0);
}
if (row == size) {
// row变成9,说明找完了整个棋盘,只要整个流程走通,就说明找到了一个可行解,触发 base case
return true;
}
if (board[row][col] != '.') {
// 如果有预设数字,不用我们穷举
return backtrack(board, row, col + 1);
}
for (char ch = '1'; ch <= '9'; ch++) {
// 如果遇到不合法的数字,就跳过
if (!isValid(board, row, col, ch)) {
continue;
}
board[row][col] = ch;
// 如果找到一个可行解,立即结束
if (backtrack(board, row, col + 1)) {
return true;
}
board[row][col] = '.';
}
// 穷举完 第1列到第9列,依然没有找到可行解,此路不通
return false;
}
// 判断 board[row][col] 是否可以填入字符 ch
boolean isValid(char[][] board, int row, int col, char ch) {
int size = 9;
for (int i = 0; i < size; i++) {
// 判断行是否存在重复
if (board[row][i] == ch) {
return false;
}
// 判断列是否存在重复
if (board[i][col] == ch) {
return false;
}
// 判断 3 x 3 方框是否存在重复
// 一般来说,判断他所在的3*3方格是否合法,需要找到自己所属于那个3*3的格子的左上角元素位置(下面就是这么做的),然后使用双重for循环遍历这9个格子去判断有没有重复。
// 这里具体实现就是使用偏移量管理,3 * (row / 3),3 * (col / 3) 找到了自己所属的3*3的格子的左上角元素位置,
// 然后i的变化就是在控制遍历以3 * (row / 3),3 * (col / 3) 为左上角顶点的方框中的不同元素。
// 至于 i/3和 i%3 , 这个其实就是使用一维坐标访问二维数组, i/row.size = 行号,i%row.size = 列号。
// 二者组合。
int boxRow = 3 * (row / 3) + i / 3;
int boxCol = 3 * (col / 3) + i % 3;
if (board[boxRow][boxCol] == ch) {
return false;
}
}
return true;
} |
Beta Was this translation helpful? Give feedback.
-
文章链接点这里:回溯算法最佳实践:解数独
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions