78、★★双层嵌套-两层for循环的回溯-LeetCode-37.解数独

题目描述

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。

来源:力扣(LeetCode)

思路

1)代码随想录:时间复杂度高

学到的内容:字符的遍历循环:第一次用到!

for(char k = '1';k <= '9';k++)

这个想法,确实第一次想不到!

实际每一个backTrack中只修改了一个 '.',有多少个点就需要传入多少次!每次都是传入一个少了一个点的新二维数组!

如果走到最后 ' . '改完了,就在最后一层的多层for循环中返回,一层一层返回true,就结束了该方法!如果遇到了false;返回上一层继续改!

就是这块儿回溯的思路一开始没想清楚

其他都是一样的,唯一没考虑到的就是 开始回溯的情况;还有一开始的boolean返回值!

已经出现了 每个位置都放不了的情况,就直接返回就好了!不用往下,boolean

        for(int i = 0;i < 9;i++){
            for(int j = 0;j < 9;j++){
                if(board[i][j] == '.'){
                    for(char k = '1';k <= '9';k++){
                        if(isTrue(board,i,j,k)){
                            board[i][j] = k;
                            if (backTrack(board)) return true;
                            board[i][j] = '.';//回溯
                        }
                    }
                    return false;
                }
            }
        }
        return true;

2)力扣答案

用数组实现位置的判断

但是该题不可避免的一个处理!

将递归方法的返回值设置为 boolean!

代码

1)回溯:参考代码回想录

class Solution {
    //不要结果集,只用更改棋盘即可
    public void solveSudoku(char[][] board) {
        //传入的即一个 类似棋盘的 二维数组,不用自己声明,模仿N皇后问题
        backTrack(board);
    }
    //回溯方法;可以在原方法中进行
    boolean backTrack(char[][] board){
        //结束条件就是棋盘没有 . ;因为不知道原来有多少 .

        for(int i = 0;i < 9;i++){
            for(int j = 0;j < 9;j++){
                if(board[i][j] == '.'){
                    for(char k = '1';k <= '9';k++){
                        if(isTrue(board,i,j,k)){
                            board[i][j] = k;
                            if (backTrack(board)) return true;
                            board[i][j] = '.';//回溯
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }
    //判断条件
    /**
    传入棋盘、行、列,也就是要放置的位置
    value表示要放置的值!
     */
    boolean isTrue(char[][] board,int row,int col,int value){
        //同一行和同一列好判断
        for(int i = 0;i < 9;i++){
            if(board[row][i] == value) return false;
        }
        for(int i = 0;i < 9;i++){
            if(board[i][col] == value) return false;
        }
        //3 * 3空间内的判断;也就是有 9个区域,使用 row和col判断所属的区域!
        //确定for for循环的区间:0、3、6三个,怎么确定;使用 row - row % 3也可以
        int r = (row / 3) * 3;
        int c = (col / 3) * 3;
        for(int i = 0;i < 3;i++){
            for(int j = 0;j < 3;j++){
                if(board[r + i][c + j] == value) return false;
            }
        }
        return true;
    }
}

2)力扣:用数组记录,某行某列或某块是否出现过某值

class Solution {
    public void solveSudoku(char[][] board) {

        // 三个布尔数组 表明 行, 列, 还有 3*3 的方格的数字是否被使用过
        boolean[][] rowUsed = new boolean[9][10];
        boolean[][] colUsed = new boolean[9][10];
        boolean[][][] boxUsed = new boolean[3][3][10];
        // 初始化
        for(int row = 0; row < board.length; row++){
            for(int col = 0; col < board[0].length; col++) {
                if(board[row][col] == '.') continue;
                int num = board[row][col] - '0';
                rowUsed[row][num] = colUsed[col][num] = boxUsed[row/3][col/3][num] = true;
            }
        }
        // 递归尝试填充数组 
        recusiveSolveSudoku(board, rowUsed, colUsed, boxUsed, 0, 0);
    }
    
    private boolean recusiveSolveSudoku(char[][]board, boolean[][]rowUsed, boolean[][]colUsed, boolean[][][]boxUsed, int row, int col){
        // 边界校验, 如果已经填充完成, 返回true, 表示一切结束
        if(col == board[0].length){
            col = 0;
            row++;
            //在这儿实现 行的增加
            if(row == board.length) return true;
        }
        // 是空则尝试填充, 否则跳过继续尝试填充下一个位置
        if(board[row][col] == '.') {
            // 尝试填充1~9
            for(int num = 1; num <= 9; num++){
                if(rowUsed[row][num] || colUsed[col][num] || boxUsed[row/3][col/3][num]) continue;
                rowUsed[row][num] = true;
                colUsed[col][num] = true;
                boxUsed[row/3][col/3][num] = true;
                    
                board[row][col] = (char)('0' + num);
                if(recusiveSolveSudoku(board, rowUsed, colUsed, boxUsed, row, col + 1)) return true;
                board[row][col] = '.';
                    
                rowUsed[row][num] = false;
                colUsed[col][num] = false;
                boxUsed[row/3][col/3][num] = false;
            }
        } else {
            //在这儿实现 列的增加,同上面递归处
            return recusiveSolveSudoku(board, rowUsed, colUsed, boxUsed, row, col + 1);
        }
        return false;
    }
}
作者:I_use_java
链接:https://leetcode-cn.com/problems/sudoku-solver/solution/hui-su-fa-jie-shu-du-by-i_use_python/
来源:力扣(LeetCode)

### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值