编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。
Note:
- 给定的数独序列只包含数字
1-9
和字符'.'
。 - 你可以假设给定的数独只有唯一解。
- 给定数独永远是
9x9
形式的。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sudoku-solver
思路:
这是一道困难题,说实话做了很久0.0
要用到回溯的算法,总的思想,可以暴力解决,每个格子都从1-9来填进去看看行不行,如果不行就换,行就往下不行再退回来
当然我们不这么做,要排除掉之前就已经不能填的格子比如在【0,2】这个格子,在第0行上要排除5,3,7这三个数,横向有了再填肯定不行
再排除纵向,有个8,再排除box范围,5,3,6,9所以最后能填的只有1,2,4三个数先填上1,然后看后一个格子,这个时候横向要多排除一个1,其他老样子排除
再往后还是这样,一直到最后一个,看看行不行,不行了就回溯,假设第一个填1一直都不行,那就换2,还不行再换4......
最后第一行找完了,那就跳到第二行,依次类推下去
class Solution { //定义三个数组,分别用于记录行使用情况,列使用情况和九个小格子使用情况 int [][] row; int [][] col; int [][] block; //定义N存储给定数组的长度 int N; boolean solve = false; //定义第一个方法,主要用于初始化数据 public void solveSudoku(char [][] board){ N = board.length; row = new int[N][N]; col = new int[N][N]; block = new int[N][N]; for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { if (board[i][j] != '.'){ //将写在board上的具体值转换为它应该加入的坐标值 int t = board[i][j] - '0' - 1; writeDown(board,i,j,t); } } } dfs(board,0,0); } private void dfs(char[][] board, int i, int j){ int box_number = (i / 3) * 3 + j / 3; //如果当前遍历到的是.那就说明要写入了,否则就走下一步 if (board[i][j] == '.'){ //可能填入的值有1-9,注意此处的循环是0-8 for (int k = 0; k < N; k++) { //判断当前的数字是否已经用过,除非行、列、格子全部都为0否则就是重复了 if (row[i][k] + col[j][k] + block[box_number][k] != 0){ continue; } writeDown(board,i,j,k); next(board,i,j); if (!solve) clear(board,i,j,k); } }else { next(board,i ,j); } } //走下一步 private void next(char[][] board,int i,int j){ //说明已经到了终点 if (i == N - 1 && j == N - 1) solve = true; else { if (j == N - 1){ dfs(board,i + 1,0); }else { dfs(board,i,j + 1); } } } //定义一个写入的函数 private void writeDown(char[][] board, int i, int j, int t) { //将该位置有数字的坐标记录到对应的记录表上去,分别是对应行,对应列,对应格子 //将坐标转为表格号 int box_number = (i / 3) * 3 + j / 3; //用1表示对应位置有数字,也就是写过了,后面遍历的时候可以跳过这个数字 row[i][t] = 1; col[j][t] = 1; block[box_number][t] = 1; //之前把值转换为了坐标值,现在要给他赋回去 board[i][j] =(char) (t + '0' + 1); } //对应的还要有一个清除的回溯用的方法 private void clear(char[][] board, int i, int j, int t) { //将该位置有数字的坐标记录到对应的记录表上去,分别是对应行,对应列,对应格子 //将坐标转为表格号 int box_number = (i / 3) * 3 + j / 3; //用1表示对应位置有数字,也就是写过了,后面遍历的时候可以跳过这个数字 row[i][t] = 0; col[j][t] = 0; block[box_number][t] = 0; board[i][j] = '.'; } }