LeetCode 36有效的数独&37解数独(八皇后问题)

简介: 判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

有效的数独



判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。


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

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

数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。


20201010134215942.png


上图是一个部分填充的有效的数独。


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


示例 1:


输入:
[
  ["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"]
]
输出: true


示例 2:


输入:
[
  ["8","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"]
]
输出: false


解释:

除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。

但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。


说明:

一个有效的数独(部分已被填充)不一定是可解的。

只需要根据以上规则,验证已经填入的数字是否有效即可。

给定数独序列只包含数字 1-9 和字符 ‘.’ 。

给定数独永远是 9x9 形式的。


分析


本题的话就是要选择合理的方式去判断是否有效。是否有效需要满足三个维度:


  • 行是否有效
  • 列是否有效
  • 3x3的格子内是否有效。


而每一个维度考虑的问题都是有相关性的,例如这一行的1-9每个数字只能出现一次,这一列也是,这个3x3也是。这样的话我们就可以通过三个boolean数组来判断各自对应的区间是否满足。因为每个区间每个数字最多只能出现一次,所以判断所有次数满足条件即可,一旦不满足就停止返回false。


当然这个有点麻烦的就是需要求当前是在第几个3x3的小方格内,经过思考(i/3)*3+j/3这个表达式可以很好的表示。


20201010140225405.png


实现代码为:


public boolean isValidSudoku(char[][] board) {
     boolean hang[][]=new boolean[9][9];//第一个是 9个坑,第二个是数字位置
   boolean lie[][]=new boolean[9][9];
   boolean fangge[][]=new boolean[9][9];
   for(int i=0;i<board.length;i++)
   {
     for(int j=0;j<board[0].length;j++)
     {
       if(board[i][j]=='.') {continue;}
       int num=board[i][j]-'1';
       if(hang[i][num]||lie[j][num]||fangge[(i/3)*3+j/3][num])
       {
         return false;
       }
       else {
         hang[i][num]=true;
         lie[j][num]=true;
         fangge[(i/3)*3+j/3][num]=true;
      }
     }
   }
   return true;
   }

20201010140412686.png

解数独



题目描述:


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

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


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

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

数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

空白格用 ‘.’ 表示。


20201010164800465.png


一个数独。


2020101016481899.png


答案被标成红色。


提示:

给定的数独序列只包含数字 1-9 和字符 ‘.’ 。

你可以假设给定的数独只有唯一解。

给定数独永远是 9x9 形式的。


分析

此题相比上一题难度稍微大一些,是一个八皇后问题的变种,对于上一题我想我们已经知道怎么判断这个是否满足规则——使用三个boolean数组判断。


但是,这一题有难度的就是需要我们手动放数据进去,手动去试探,并且每一步放置之后对后面都有影响。我们该如何思考这种问题呢?


dfs回溯,八皇后问题其实就是典型的回溯过程,而这种二维的回溯需要考虑一些问题,我们对于每一行每一行考虑。 每一行已经预有一些数据事先标记,在从开始试探放值,满足条件后向下递归试探。一直到结束如果都满足那么就可以结束返回数组值。


对于八皇后问题后面还会出一篇细讲,这里的话有两点需要注意的在这里提一下:

  • 用二维两个参数进行递归回溯判断起来谁加谁减比较麻烦,所以我们用一个参数index用它来计算横纵坐标进行转换,这样就减少二维递归的一些麻烦。
  • 回溯是一个来回的过程,在回来的过程正常情况需要将数据改回去,但是如果已经知道结果就没必要再该回去可以直接停止放置回溯造成值的修改(这里我用了一个isfinish的boolean类型进行判断)。


具体ac代码为:


boolean isfinish=false;
boolean hang[][]=new boolean[9][10];//第一个是 9个坑,第二个是数字位置
boolean lie[][]=new boolean[9][10];
boolean fangge[][]=new boolean[9][10];
public  void solveSudoku(char[][] board) {
   //首先遍历一遍 将已有元素的行列信息提前做处理
   for(int i=0;i<board.length;i++)
   {
     for(int j=0;j<board[0].length;j++)
     {
       if(board[i][j]=='.') {continue;}
       int k=board[i][j]-'0';   
       hang[i][k]=true;
       lie[j][k]=true;
       fangge[(i/3)*3+j/3][k]=true;
     }
   }  
   dfs(0,board);   
   }
private void dfs( int index, char[][] board) {
  if(isfinish) return;//已有结果不需要再计算
  if(index==81) {//到达最后一个前面都满足条件说明可以停止了
    isfinish=true;
    return;
  }
  int i=index/9;//行
  int j=index%9;//列
  if(board[i][j]!='.')//已经有数字
  {
    dfs( index+1, board);
  }
  else {//此处需要补充数字
    for(int k=1;k<10;k++)
    { 
       //如果不满足直接跳过
       if(hang[i][k]||lie[j][k]||fangge[(i/3)*3+j/3][k]) {continue;}
       //满足临时试探修改 进行回溯
       board[i][j]=(char) (k+'0'); 
       hang[i][k]=true;
         lie[j][k]=true;
       fangge[(i/3)*3+j/3][k]=true;
       //递归回溯
       dfs( index+1, board);
       //递归完成后需要复原,如果结束了不需要复原直接停止
       if(isfinish)return;
       board[i][j]='.'; 
       hang[i][k]=false;
       lie[j][k]=false;
       fangge[(i/3)*3+j/3][k]=false;
    }
  }
}

20201010171943181.png


结语



好啦,本次打卡结束,对于八皇后问题是一个经典的回溯递归问题,不久就会出一篇相关内容问题的具体分析,敬请来公众号bigsai分享。如果想加入打卡还请加笔者公众号bigsai 回复进群。一起打卡,回复bigsai获取5G的pdf学习资源。


20201010195712376.png


目录
相关文章
|
6月前
|
Java
leetcode-37:解数独
leetcode-37:解数独
47 0
|
1月前
|
存储 算法 C++
Leetcode第三十六题(有效的数独)
这篇文章介绍了如何使用C++编写一个算法来验证一个9x9数独是否有效,遵循数独的规则,即数字1-9在每一行、每一列和每个3x3宫内只能出现一次。
42 0
|
3月前
|
存储 算法 索引
LeetCode第36题有效的数独
这篇文章介绍了LeetCode第36题"有效的数独"的解题方法,通过使用三个二维数组分别记录每一行、每一列以及每个3x3宫格内1-9数字出现的次数,来验证给定数独是否有效。
|
5月前
|
算法
力扣经典150题解析之三十四:有效的数独
力扣经典150题解析之三十四:有效的数独
43 0
|
5月前
|
算法
【经典LeetCode算法题目专栏分类】【第3期】回溯问题系列:单词搜索、N皇后问题、判断有效数独、解数独
【经典LeetCode算法题目专栏分类】【第3期】回溯问题系列:单词搜索、N皇后问题、判断有效数独、解数独
|
6月前
leetcode-36:有效的数独
leetcode-36:有效的数独
49 0
|
JavaScript 前端开发 算法
LeetCode 37.解数独(注释完整+JavaScript解题)
LeetCode 37.解数独(注释完整+JavaScript解题)
78 0
|
算法
LeetCode 37 解数独 循环+回溯算法
LeetCode 37 解数独 循环+回溯算法
57 0
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行