leetcode算法整理37解数独

纵饮孤独 提交于 2020-02-02 21:46:16

LEETCODE算法注解37:

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

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

    数字 1-9 在每一行只能出现一次。
    数字 1-9 在每一列只能出现一次。
    数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。

Note:

    给定的数独序列只包含数字 1-9 和字符 '.' 。
    你可以假设给定的数独只有唯一解。
    给定数独永远是 9x9 形式的。
    
此处不给图片了。

此处讲一下个人做这个题的感觉,其实说思路也不算难:假如给A空格填入合适的值,接下来去填B空格,发现没有合适的值可填,要返回A修改值,再去填B的值。
但是这个代码好难写啊,真令人头大,几天没写出来,看了官方答案,写的真好啊。

回溯法是解决此题的关键,也就是我上面说的思路。但是大多数同学不了解具体过程,
此处以题目给出的例子来详细解释一下。(函数名字参考下面代码)
在这里插入图片描述在这里插入图片描述
代码逻辑:
现在准备好写回溯函数了
backtrack(row = 0, col = 0)

从最左上角的方格开始 row = 0, col = 0。直到到达一个空方格。

从1 到 9 迭代循环数组,尝试放置数字 d 进入 (row, col) 的格子。

    如果数字 d 还没有出现在当前行,列和子方块中:
        将 d 放入 (row, col) 格子中。
        记录下 d 已经出现在当前行,列和子方块中。
        如果这是最后一个格子row == 8, col == 8 :
            意味着已经找出了数独的解。
        否则
            放置接下来的数字。
        如果数独的解还没找到(即没有合适的值填入空格):
        将最后的数从 (row, col) 移除。

代码(附有详细注释)

class Solution {
 
  int n = 3;
  int N = n * n;
  //因为可填充的最大值时9,所以为N+1=10.
  int [][] rows = new int[N][N + 1];
  int [][] columns = new int[N][N + 1];
  int [][] boxes = new int[N][N + 1];

  char[][] board;

  boolean sudokuSolved = false;//用来判定是否结束的标记,初始为false,表明没结束,为true结束
  //判定能否填入的方法
  public boolean couldPlace(int d, int row, int col) 
  {
    int idx = (row / n ) * n + col / n;//小九宫格
    //d是填入的数字,row是行,含义:row行d数字  此坐标为不为0,代表已经填入 为0代表未曾填入 ,当三者和为0时代表此值可以填入
    return rows[row][d] + columns[col][d] + boxes[idx][d] == 0;
  }
  //填入的方法
  public void placeNumber(int d, int row, int col) 
  {

    int idx = (row / n ) * n + col / n;//小九宫格
    //初始化时三个数组值全为0, ++的含义是此行.此列.此九宫格已经有d这个数字了
    rows[row][d]++;
    columns[col][d]++;
    boxes[idx][d]++;
    board[row][col] = (char)(d + '0');//将数字填入board中
  }
  //填充失败,重新归‘.’
  public void removeNumber(int d, int row, int col) 
  {

    int idx = (row / n ) * n + col / n;
    rows[row][d]--;
    columns[col][d]--;
    boxes[idx][d]--;
    board[row][col] = '.';
  }

  public void placeNextNumbers(int row, int col) 
  {
    //成功填充到最后一位
    if ((col == N - 1) && (row == N - 1)) {
      sudokuSolved = true;
    }
    //继续填充下一位
    else {
      if (col == N - 1) backtrack(row + 1, 0);
      else backtrack(row, col + 1);
    }
  }

  public void backtrack(int row, int col) 
  {
    //此处未曾填充,填充范围1~9
    if (board[row][col] == '.') 
    {
      for (int d = 1; d < 10; d++) 
      {
        if (couldPlace(d, row, col)) //判定此值填入,行 列 小九宫都没有重复
        {
          placeNumber(d, row, col);//填充当前值
          placeNextNumbers(row, col);//填充下一个值
          if (!sudokuSolved) removeNumber(d, row, col);//不满足要求,移除数字
        }
      }
    }
    else placeNextNumbers(row, col);//此处已被填充,填充下一个值
  }

  public void solveSudoku(char[][] board) 
  {
    this.board = board;
    //先双重循环将所有数值整理出来,填入行.列.小九宫格中。
    for (int i = 0; i < N; i++) 
    {
      for (int j = 0; j < N; j++) 
      {
        char num = board[i][j];
        if (num != '.') {
          int d = Character.getNumericValue(num);
          placeNumber(d, i, j);//填入的方法
        }
      }
    }
    backtrack(0, 0);//从第一个空格开始回溯
  }
}

参考原地址

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!