20210621力扣第37题:解数独(java)

  • Post author:
  • Post category:java




题目:解数独

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

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

数字 1-9 在每一行只能出现一次。

数字 1-9 在每一列只能出现一次。

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

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

在这里插入图片描述

输入: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"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver

在这里插入图片描述

提示:

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




理解题意

这道题和36题相比,我们已经知道了,行列 块索引,多了一步就是需要填写数独,且已经保证数独只有唯一的解了,所以我们不需要在判断数独是否是个有效的了,现在需要的是把数字填进去,并且保证 行/列/块 中的数字不能重复。



1.想法

我猜想:还是使用hashmp存放数值,根据36题的方式写,又想到,当这个空格填入一个值之后,后面可能再填这个值,那么就会出错,可能需要不断的试探,难道用一个while循环?

看了下评论和题解,本题使用到的是递归和回溯。

先填入一个值,循环再填入一个值,当发现没有值可以填时,再向前回溯,重新填入值。

我没有写出来。就看了官方的结题思路:

  • 定义行 列 块的布尔数组,定义一个list,为了存放没有填的值‘.’的坐标
  • 遍历这个board,如果是‘.’,list就存入坐标,如果有值,就把他们的布尔数组设置为true;
  • 定义回溯的方法:先写终止条件:当填入的数字个数等于 ‘.’的个数时 终止
  • 拿出‘.’的坐标,循环数字1-9填入,递归,然后在进行回溯,看看能不能满足条件。

将代码附上:

class Solution {
     //行 列 3*3的格子是否会重复
        private boolean[][] row = new boolean[9][9];
        private boolean[][] col = new boolean[9][9];
        private boolean[][][] block = new boolean[3][3][9];
        boolean valid =false;
        List<int[]> list =new ArrayList<int[]>();

    public void solveSudoku(char[][] board) {
       for(int i = 0 ; i< 9 ; i++)
       {
           for(int j = 0 ;  j < 9 ; j++)
           {
               if(board[i][j]=='.')
               {
                   list.add(new int[]{i,j});//把这个空坐标 添加到list中;
               }
               else
               {
                   int num = board[i][j]-'0'-1;//获取到这个位置的数字;
                   //因为是9*9 出现的那个数字应该是独一无二的 -1表示这个数字 在数组中出现的位置的数字
                   row[i][num] = col[j][num] = block[i/3][j/3][num] = true;
               }
           }
       }
       dfs(board,0);
    }

       public void dfs(char[][] board,int pos)
       {
           if(pos ==list.size())//就是当填入的数字等于 开始空出来的数字的时候就可以返回true了
           {
               valid = true;
               return;
           }

            int[] li = list.get(pos);//获取到那个 空的值   一个行 一个列
            int  i = li[0]; int j=li[1];
            //将1-9循环填入这个数字 看行不行
            for(int digit = 0 ; digit<9&& !valid;digit++)
            {
                if(!row[i][digit]&&!col[j][digit]&&!block[i/3][j/3][digit])
                {
                    row[i][digit] =col[j][digit]=block[i/3][j/3][digit]=true;
                    board[i][j] = (char) (digit+'0'+1);
                    dfs(board,pos+1);
                    row[i][digit] = col[j][digit]=block[i/3][j/3][digit] = false; 


                }
            }

        }

}

在写的过程中,需要注意的是

for(int digit = 0 ; digit<9&& !valid;digit++)

其中的valid不能省略,valid标记的是这个地方有没有走过。当我编程吧valid去掉后就出错了。还需要注意的是board是char类型的,转换需要注意。



总结

开始的时候不太会写代码,想了一会儿写不出,就看了答案,答案好像也不是很懂,就照着答案写了一遍,写了一遍之后,再仔细看各个步骤的意思,就能够懂一些了,其中使用的回溯法,当不满足时,就将当前在设置为false,这种回溯方法在之前就遇到过, 虽然知道使用回溯方法,但是遇到问题的时候却不会使用了,以后需要强制自己进行此类问题的思考才行,不能太依赖答案呀。还有我看答案第二个方法使用了位运算来解,和上面思想有点相似,不同的是上面是用布尔类型,位运算代替了布尔类型来进行存储,我看了下,没有看懂的地方在哪些位运算符上,所以这里就没有列出了,先把这些题做出来,然后再完善吧。希望我能记住本题是怎么回溯的。



版权声明:本文为duan18291953965原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。