• LeetCode算法题解(回溯,难点)|LeetCode37. 解数独


    LeetCode37. 解数独

    题目链接:37. 解数独
    题目描述:

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

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

    1. 数字 1-9 在每一行只能出现一次。
    2. 数字 1-9 在每一列只能出现一次。
    3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

    数独部分空格内已填入了数字,空白格用 '.' 表示。

    示例 1:

    输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
    输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
    解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
    
    
    

    提示:

    • board.length == 9
    • board[i].length == 9
    • board[i][j] 是一位数字或者 '.'
    • 题目数据 保证 输入数独仅有一个解

    算法分析

    利用回溯法确定并放置每个格子能放值的数字。

    回溯的返回值类型为boolean,只要找到一个符合的条件就直接返回true。

    利用双重for循环搜索每个格子的位置,并判断该格子是否是空格,如果不是空格不是空格直接跳过这一个格子。

    1. public boolean backTravel(int x, int y, char[][] board) {
    2. for(int i = x; i < board.length; i++) {
    3. for(int j = y; j < board[0].length; j++) {//遍历每一个格子
    4. if(board[i][j] != '.') continue;
    5. ....
    6. }
    7. }
    8. }

    如果该格子是空格子,判断该格子是否可以放入1-9当中的数字如果可以,放入数字,然后递归、回溯。

    1. for(char ch = '1'; ch <= '9'; ch++) {
    2. if(canPut(ch, i, j, board)) {//判断当前空白格是否可以用1-9数字来填入
    3. board[i][j] = ch;//如果可以将数字字符填入空格内
    4. if(backTravel(i, y, board)) return true;//然后递归,如果递归返回的是true那么说明找到了解决方案,直接返回true
    5. board[i][j] = '.';//如果没找到,进行回溯
    6. }
    7. }

    判断是否可以放数字的函数:

    1. public boolean canPut(char ch, int x, int y, char[][] board) {//判断当下坐标是否可以放置ch
    2. for(int i = 0; i < board.length; i++) {//判断这一列是否出现过ch
    3. ...
    4. }
    5. for(int j = 0; j < board[0].length; j++) {//判断这一行是否出现过ch
    6. ...
    7. }
    8. //找到当前格子所属的3*3宫格的左上角坐标
    9. int startx = (x/3)*3;
    10. int starty = (y/3)*3;
    11. for(int i = startx; i < startx + 3; i++) {//判断当前坐标所属的3*3宫格内是否出现ch
    12. for(int j = starty; j < starty + 3; j++) {
    13. ...
    14. }
    15. }
    16. return true;
    17. }

    完整的代码如下:

    1. class Solution {
    2. public boolean canPut(char ch, int x, int y, char[][] board) {//判断当下坐标是否可以放置ch
    3. for(int i = 0; i < board.length; i++) {//判断这一列是否出现过ch
    4. if(board[i][y] == ch)
    5. return false;
    6. }
    7. for(int j = 0; j < board[0].length; j++) {//判断这一行是否出现过ch
    8. if(board[x][j] == ch)
    9. return false;
    10. }
    11. //找到当前格子所属的3*3宫格的左上角坐标
    12. int startx = (x/3)*3;
    13. int starty = (y/3)*3;
    14. for(int i = startx; i < startx + 3; i++) {//判断当前坐标所属的3*3宫格内是否出现ch
    15. for(int j = starty; j < starty + 3; j++) {
    16. if(board[i][j] == ch)
    17. return false;
    18. }
    19. }
    20. return true;
    21. }
    22. public boolean backTravel(int x, int y, char[][] board) {
    23. for(int i = x; i < board.length; i++) {
    24. for(int j = y; j < board[0].length; j++) {//遍历每一个格子
    25. if(board[i][j] != '.') continue;
    26. for(char ch = '1'; ch <= '9'; ch++) {
    27. if(canPut(ch, i, j, board)) {//判断当前空白格是否可以用1-9数字来填入
    28. board[i][j] = ch;//如果可以将数字字符填入空格内
    29. if(backTravel(i, y, board)) return true;//然后递归,如果递归返回的是true那么说明找到了解决方案,直接返回true
    30. board[i][j] = '.';//如果没找到,进行回溯
    31. }
    32. }
    33. //1-9都不能填入当前空格,说明找不到解诀数独的方法,返回false
    34. return false;
    35. }
    36. }
    37. //遍历完了都没有返回false,说明找到了合适的棋盘位置,返回true
    38. return true;
    39. }
    40. public void solveSudoku(char[][] board) {
    41. backTravel(0, 0, board);
    42. }
    43. }

    总结

    这道题的难点是对每个空格子该放入哪个数字,以及什么时候结束回溯的操作。

  • 相关阅读:
    js中循环判断找到满足条件的单项后结束循环
    ArrayList知识点(面试)
    2023/10/12 -- ARM
    制作游戏demo的心得
    umi3项目axios 请求参数序列化参数
    nginx降权及匹配php
    ES9023发烧级音频DAC声卡解码器资料
    uniapp 封装request请求
    【zeno】zeno如何为自定义节点添加功能(apply函数和ZENDEFNODE初探)
    通过Power Platform自定义D365 CE 业务需求 - 10.使用Power Apps和Dynamics 365的集成
  • 原文地址:https://blog.csdn.net/2201_76107580/article/details/134339290