递归算法学习——有效的数独,解数独

一,有效的数独

1.题意

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

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

注意:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 空白格用 '.' 表示。

2.解释

有效的数独这道题的其实是不需要用到递归的。这道题其实就是一个判断题,要保证的的便是再一个9*9大小的二维数组里1~9这九个数字在每一行每一列每一个九宫格里面只出现了一次。如以下例子:

黑实线分割开的就是一个九宫格。这个数独的每一行每一列出现的数据都是唯一的,所以这个数独是有效的。

3.题目接口

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {

    }
};

4.解题思路及代码

要解决这道题,我们首先就需要三个数组。这三个数组记录的便是我们是否在某一行,某一列,某一个九宫格里面是否又出现过某一个数字。在遍历过程中若出现了某一个数字便将这一行,这一列,这一个九宫格的第出现的数字的这一个位置标记为true。下次如果还会遍历同样的数字便返回false。如下代码:

class Solution {
public:
    bool row[9][10];
    bool col[9][10];
    bool grid[3][3][10];
    bool isValidSudoku(vector<vector<char>>& board) {
        for(int i = 0;i<9;i++)
        {
            for(int j = 0;j<9;j++)
            {
                if(board[i][j]!='.')//当遍历到的数据不是'.'时便可以进入判断
                {
                    int num = board[i][j]-'0';//将字符转换为数字
                    if(row[i][num]||col[j][num]||grid[i/3][j/3][num])//当这个数字在行,列,九宫格任何一个位置上出现时便可以返回false
                    {
                        return false;
                    }
                     row[i][num] = col[j][num] = grid[i/3][j/3][num] = true;//遍历过后将这一行这一列这个九宫格上的这个数字记录下来
                }
            }
        }

        return true;
    }
};

在这里大家可能比较疑惑的便是grid[i/3][j/3][num]了。其实这里便是将九宫格坐标化了,当行数和列数都在1~3时对标的便是下标0,4~6对标的便是下标1,6~8对标的便是下标2。在一个9*9的格子里面有九个九宫格,按照上面的分法下标分别是(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)。

二,解数独

1.题意

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

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

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

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

2.解释

这一道题便是让我们来填入数字来解决数独问题了。上面的有效的数独不需要用到递归的方式来解决但是这道题便要使用递归来解决了。

3.题目接口

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {

    }
};

4.解题思路及代码

先将代码写出再来解释,代码:

class Solution {
public:
    bool row[9][10];
    bool col[9][10];
    bool grid[3][3][10];

    void solveSudoku(vector<vector<char>>& board) {
        for(int i = 0;i<9;i++)//先将数独里面出现的数字标记下来
        {
            for(int j = 0;j<9;j++)
            {
                if(board[i][j]!='.')
                {
                    int num = board[i][j]-'0';
                    row[i][num] = col[j][num] = grid[i/3][j/3][num] = true;
                }
            }
        }  
        dfs(board); 
    }

    bool dfs(vector<vector<char>>&board)
    {
        for(int i = 0;i<9;i++)
        {
            for(int j = 0;j<9;j++)
            {
                  if(board[i][j] == '.')
                  {
                      for(int num = 1;num<=9;num++)
                      {
                          if(!row[i][num]&&!col[j][num]&&!grid[i/3][j/3][num])
                          {
                              board[i][j] = num+'0';
                              row[i][num] = col[j][num] = grid[i/3][j/3][num] = true;
                              if(dfs(board)) return true;
                              board[i][j] = '.';//当走到这里时便是因为这一层填的数字的不到结果所以要将这个位置的值改回'.'标记改为false。
                              row[i][num] = col[j][num] = grid[i/3][j/3][num] = false;

                          }
                      }
                      return false;//当遍历到的这一个格子九个数字都不能填时便返回false调整上一层的值
                  }
            }
        }
        return true;
    }
};

在这一道题里面最让人难以理解的便是没有递归出口,因为递归必须要有出口才能返回到上一层。但是这道题的代码里面似乎没有是吧。其实不是的,这道题只是没有显示的写出递归出口。它是使用两个for循环来隐式的作为递归出口了,当一个棋盘被遍历完了以后或者不能得到结果时便会返回到上一层重新操作。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值