news 2026/5/1 9:59:06

回溯算法--解数独

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
回溯算法--解数独

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

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

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

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

难点

N皇后问题和解数独问题对比

一、 求解策略:要“全集”还是要“特例”?

  • N皇后 (void):我们需要知道一共有多少种摆法。这意味着即使找到了一组解,也不能停,必须回溯回去,继续探索其他分支。所以不需要返回值,用全局变量result收集。

  • 解数独 (bool):数独只需要填满棋盘即可。一旦找到一条通往叶子节点的路径(即棋盘填满),就立刻层层返回true,通过if (backtracking(...)) return true;阻断后续无意义的尝试。

二、 维度差异:一维推进 vs 二维扫描这是两道题代码结构差异的根源。

  • N皇后(一维问题):

    • 规则特性:每一行只能且必须放一个皇后。

    • 推论:一旦第row行确定了位置,该行的任务就结束了,直接递归进入row + 1

    • 代码体现:递归参数只需要row。我们在思维上把二维棋盘压扁成了 N 个的一维决策步骤。

  • 解数独(二维问题):

    • 规则特性:每一行有多个空缺,且空缺位置不固定。

    • 推论:我们不能简单地“处理完这一行去下一行”。我们需要精确定位到棋盘上的每一个坐标(i, j)

    • 代码体现通用写法:每次递归都双重循环for i, for j扫描整个棋盘去找下一个 。

三、 树的宽度与操作粒度

  • N皇后:是在决定“这一行的皇后放在哪一”。(树的宽度是 N,即列数)。

  • 解数独:是在决定“这一个格子填入哪一个数字”。(树的宽度是 9,即数字 1-9)。

代码

参数

bool backtracking(vector<vector<char>>& board) {

终止条件

本题目其实不要终止条件,如果双重循环跑完了,都没有遇到 return false,说明棋盘里没有 '.' 了(填满了)

单层递归逻辑

for (int i = 0; i < board.size(); i++) { // 遍历行 for (int j = 0; j < board[0].size(); j++) { // 遍历列 // 步骤 1: 寻找空白格('.' 表示还没填数字) if (board[i][j] == '.') { // 步骤 2: 尝试填入数字 '1' 到 '9' for (char k = '1'; k <= '9'; k++) { // 步骤 3: 检查在 (i, j) 放数字 k 是否符合数独规则 if (isValid(i, j, k, board)) { board[i][j] = k; // 【做选择】:放置数字 k // 步骤 4: 递归调用 // 如果填入 k 之后,后续的递归也能成功填满棋盘,说明找到解了 if (backtracking(board)) return true; board[i][j] = '.'; // 【撤销选择】:回溯 // 如果上面的 backtracking 返回 false,说明刚才填 k 导致后面无解 // 所以要把 k 拿走,恢复成 '.',以便下一次循环尝试 k+1 } } // 步骤 5: 关键点 // 如果 1-9 都试过了,都不合法(或者导致后续无解),说明当前这一步就死路一条 // 返回 false 给上一层递归,告诉它“你之前填的数有问题,换一个吧” return false; } } }

isValid函数

其中定位当前格子所属的 3x3 九宫格的左上角起点记一下

bool isValid(int row, int col, char val, vector<vector<char>>& board) { for (int i = 0; i < 9; i++) { // 1. 检查同一列是否有重复 if (board[i][col] == val) return false; // 2. 检查同一行是否有重复 if (board[row][i] == val) return false; // 3. 检查所在的 3x3 九宫格是否有重复 // 公式说明: // (row / 3) * 3 用于定位该点所属九宫格的“左上角”行坐标 // (col / 3) * 3 用于定位该点所属九宫格的“左上角”列坐标 // board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] 是更高级的写法, // 但你代码中原本的写法也很直观,如下: } // 定位当前格子所属的 3x3 九宫格的左上角起点 int startRow = (row / 3) * 3; int startCol = (col / 3) * 3; // 遍历这个 3x3 的小方格 for (int i = startRow; i < startRow + 3; i++) { for (int j = startCol; j < startCol + 3; j++) { if (board[i][j] == val) { return false; } } } return true; // 完美,通过所有检查 }

完整代码

class Solution { private: // 回溯核心函数:尝试填充棋盘,如果填满且有效返回 true,否则返回 false bool backtracking(vector<vector<char>>& board) { // 双重循环遍历整个棋盘(9x9) for (int i = 0; i < board.size(); i++) { // 遍历行 for (int j = 0; j < board[0].size(); j++) { // 遍历列 // 步骤 1: 寻找空白格('.' 表示还没填数字) if (board[i][j] == '.') { // 步骤 2: 尝试填入数字 '1' 到 '9' for (char k = '1'; k <= '9'; k++) { // 步骤 3: 检查在 (i, j) 放数字 k 是否符合数独规则 if (isValid(i, j, k, board)) { board[i][j] = k; // 【做选择】:放置数字 k // 步骤 4: 递归调用 // 如果填入 k 之后,后续的递归也能成功填满棋盘,说明找到解了 if (backtracking(board)) return true; board[i][j] = '.'; // 【撤销选择】:回溯 // 如果上面的 backtracking 返回 false,说明刚才填 k 导致后面无解 // 所以要把 k 拿走,恢复成 '.',以便下一次循环尝试 k+1 } } // 步骤 5: 关键点 // 如果 1-9 都试过了,都不合法(或者导致后续无解),说明当前这一步就死路一条 // 返回 false 给上一层递归,告诉它“你之前填的数有问题,换一个吧” return false; } } } // 步骤 6: 终止条件 // 如果双重循环跑完了,都没有遇到 return false,说明棋盘里没有 '.' 了(填满了) // 这就是我们想要的解,直接返回 true return true; } // 辅助函数:判断在 board[row][col] 填入字符 val 是否合法 bool isValid(int row, int col, char val, vector<vector<char>>& board) { for (int i = 0; i < 9; i++) { // 1. 检查同一列是否有重复 if (board[i][col] == val) return false; // 2. 检查同一行是否有重复 if (board[row][i] == val) return false; // 3. 检查所在的 3x3 九宫格是否有重复 // 公式说明: // (row / 3) * 3 用于定位该点所属九宫格的“左上角”行坐标 // (col / 3) * 3 用于定位该点所属九宫格的“左上角”列坐标 // board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] 是更高级的写法, // 但你代码中原本的写法也很直观,如下: } // 定位当前格子所属的 3x3 九宫格的左上角起点 int startRow = (row / 3) * 3; int startCol = (col / 3) * 3; // 遍历这个 3x3 的小方格 for (int i = startRow; i < startRow + 3; i++) { for (int j = startCol; j < startCol + 3; j++) { if (board[i][j] == val) { return false; } } } return true; // 完美,通过所有检查 } public: void solveSudoku(vector<vector<char>>& board) { backtracking(board); } };
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/1 8:15:09

BetterNCM插件管理器:重塑你的音乐播放体验

BetterNCM插件管理器&#xff1a;重塑你的音乐播放体验 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 还在为网易云音乐的单调界面而烦恼&#xff1f;想要解锁更多个性化功能和视觉美…

作者头像 李华
网站建设 2026/5/1 6:51:41

Mac用户福音:云端跑Llama3中文版,苹果芯片不再受限

Mac用户福音&#xff1a;云端跑Llama3中文版&#xff0c;苹果芯片不再受限 你是不是也遇到过这种情况&#xff1f;作为一名设计师&#xff0c;手里的M1 MacBook Pro用起来顺手得不行——轻薄、续航强、屏幕漂亮。但一想搞点AI创作&#xff0c;比如让大模型帮你生成设计灵感图、…

作者头像 李华
网站建设 2026/5/1 7:50:58

3小时全面指南:如何让老款Mac完美兼容最新macOS系统

3小时全面指南&#xff1a;如何让老款Mac完美兼容最新macOS系统 【免费下载链接】OpenCore-Legacy-Patcher 体验与之前一样的macOS 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 想要让2012-2015年款的Mac设备重新支持最新macOS系统吗&am…

作者头像 李华
网站建设 2026/5/1 8:02:18

想做ASMR或纪录片旁白?试试科哥开发的Voice Sculptor语音工具

想做ASMR或纪录片旁白&#xff1f;试试科哥开发的Voice Sculptor语音工具 1. 引言&#xff1a;为什么你需要一个可定制的声音合成工具&#xff1f; 在内容创作日益个性化的今天&#xff0c;声音已成为塑造品牌、传递情感的重要载体。无论是制作冥想引导音频、纪录片旁白&…

作者头像 李华
网站建设 2026/5/1 6:05:40

YOLOv8部署案例:交通监控车辆行人检测实战

YOLOv8部署案例&#xff1a;交通监控车辆行人检测实战 1. 引言 随着城市化进程的加快&#xff0c;智能交通系统对实时目标检测的需求日益增长。在复杂的城市道路环境中&#xff0c;如何高效、准确地识别车辆与行人&#xff0c;成为提升交通安全与管理效率的关键。传统方法受限…

作者头像 李华
网站建设 2026/5/1 7:13:29

小白必看!AutoGen Studio保姆级教程:从零搭建AI代理

小白必看&#xff01;AutoGen Studio保姆级教程&#xff1a;从零搭建AI代理 1. 引言&#xff1a;为什么选择AutoGen Studio&#xff1f; 在当前多代理&#xff08;Multi-Agent&#xff09;系统快速发展的背景下&#xff0c;如何高效构建具备协作能力的AI代理团队成为开发者关…

作者头像 李华