回溯算法最佳实践:解数独 :: 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.
-
打卡,感谢! |
Beta Was this translation helpful? Give feedback.
-
@typedefstruct32 你的想法是对的,一般我在写的时候都是在类里写一个变量,然后算出一个结果就把他加进去,如果只是单纯回溯的话一定会变回最开始的样子,因为每次修改后都会把它改回去 |
Beta Was this translation helpful? Give feedback.
-
请问周围一圈的位置判断公式怎么得出的 |
Beta Was this translation helpful? Give feedback.
-
1 |
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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
文章链接点这里:回溯算法最佳实践:解数独
评论礼仪 见这里,违者直接拉黑。
Beta Was this translation helpful? Give feedback.
All reactions