算法系列之回溯算法求解数独及所有可能解

_20250314_222542.png

有没有对数独感兴趣的朋友呢?数独作为一款经典的逻辑游戏,其目标是在一个9x9的方格中填入数字1至9,确保每一行、每一列以及每一个3x3的子网格中都包含这些数字且不重复。尽管数独的规则看似简单,但编写一个能够自动求解数独的程序却是一项颇具挑战性的任务。本文将深入探讨如何运用回溯算法来实现数独的自动求解。

wps_f4zzDJ7hhN.png

数独求解算法及步骤

我们使用一个二维数组来表示数独的表格,空位置填充0。

数独求解的核心算法是回溯算法。回溯算法是一种通过逐步构建解决方案并在遇到冲突时回退的算法。具体来说,我们尝试在空格中填入一个数字,然后递归地继续填充下一个空格。如果在某个步骤中发现无法继续填充,则回退到上一步并尝试其他数字。

  • 算法步骤
  1. 寻找空格:我们循环数独的所有单元格,如果数组的值为0的话则此格未填写数字。

  2. 尝试填入数字:对于这个空格,尝试填入1到9中的一个数字。

  3. 检查数字的正确性:检查填入的数字是否与当前行、列和3x3子网格中的数字有重复。

  4. 递归求解:如果没有重复,则递归地继续填充下一个空格。

  5. 回溯:如果在某个步骤中发现无法继续填充,则回退到上一步并尝试其他数字。

Java代码实现

我们使用一个二维数组来表示数独,有一种只求解数独的方法及求解不是唯一解的所有可行解的方法。代码如下


/*** 数独求解*/
public class SudokuSolver {/*** 检查数独元素的正确性,及每行、每列、每九宫格的唯一性*/public static boolean checkValue(int[][] sudoku,int value,int row,int col){//检验当前元素所在行for (int i = 0; i < 9; i++) {if(sudoku[row][i] == value){return false;}}//检验当前元素所在列for (int i = 0; i < 9; i++) {if(sudoku[i][col] == value){return false;}}//检验当前元素所在九宫格for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {// 如果当前元素所在九宫格有值,则返回falseif(sudoku[row/3*3+i][col/3*3+j] == value){return false;}}}return true;};/*** 回溯算法求解数独*/public static boolean solveSudokuSingleSec(int[][] sudoku) {//递归回溯法求解数独,循环遍历81个元素,如果当前元素为0,则尝试1-9的值,如果符合要求,则递归求解,否则返回上一层继续尝试for (int i = 0; i < 9; i++) {for(int j = 0; j < 9; j++){//如果当前元素为0,则尝试1-9的值,如果符合要求,则递归求解,否则返回上一层继续尝试if(sudoku[i][j]== 0){for (int k =1;k<=9;k++){//如果符合要求,则递归求解,否则返回上一层继续尝试if(checkValue(sudoku,k,i,j)){sudoku[i][j] = k;if(solveSudokuSingleSec(sudoku)){return true;}// 回溯sudoku[i][j] = 0;}}// 无法继续填充,则回退到上一步并尝试其他数字。return false;}}}// 找到一个解,则返回true,无需继续回溯return true;}/***回溯算法求解数独的所有可能解*/public static void solveSudokuSec(int[][] sudoku, List<int[][]> result) {// 递归回溯法求解数独,循环遍历81个元素,如果当前元素为0,则尝试1-9的值,如果符合要求,则递归求解,否则返回上一层继续尝试for (int i = 0; i < 9; i++) {for(int j = 0; j < 9; j++){if(sudoku[i][j]== 0){for (int k =1;k<=9;k++){if(checkValue(sudoku,k,i,j)){sudoku[i][j] = k;// 递归求解solveSudokuSec(sudoku,result);// 回溯sudoku[i][j] = 0;}}// 无法继续填充,则回退到上一步并尝试其他数字。return;}}}// 找到一个解,记录并添加到集合中int[][] resultArray = new int[9][9];for (int row = 0; row < 9; row++) {System.arraycopy(sudoku[row], 0, resultArray[row], 0, 9);}result.add(resultArray);}public static void main(String[] args) {int[][] initArraySingle = new int[][]{{8,0,0,0,0,0,0,0,0},{0,0,3,6,0,0,0,0,0},{0,7,0,0,9,0,2,0,0},{0,5,0,0,0,7,0,0,0},{0,0,0,0,4,5,7,0,0},{0,0,0,1,0,0,0,3,0},{0,0,1,0,0,0,0,6,8},{0,0,8,5,0,0,0,1,0},{0,9,0,0,0,0,4,0,0}};int[][] initArray = new int[][]{{8,0,0,0,0,0,0,0,0},{0,0,3,6,0,0,0,0,0},{0,7,0,0,9,0,2,0,0},{0,8,0,0,0,7,0,0,0},{0,0,0,0,4,5,7,0,0},{0,0,0,1,0,0,0,3,0},{0,0,1,0,0,0,0,6,8},{0,0,8,5,0,0,0,1,0},{0,9,0,0,0,0,4,0,0}};// 回溯算法求解数独solveSudokuSingleSec(initArraySingle);for (int i = 0; i < 9; i++) {for (int j = 0; j < 9; j++) {System.out.print(initArraySingle[i][j]+" ");}System.out.println();}List<int[][]> result = new ArrayList<>();// 回溯算法求解数独的所有可能解solveSudokuSec(initArray,result);System.out.println("共"+result.size()+"种解法");for (int i = 0; i < result.size(); i++){System.out.println("解法"+(i+1)+":");for (int j = 0; j < 9; j++) {for (int k = 0; k < 9; k++) {System.out.print(initArraySingle[j][k]+" ");}System.out.println();}};}}

求解结果如下:

8 1 2 7 5 3 6 4 9 
9 4 3 6 8 2 1 7 5 
6 7 5 4 9 1 2 8 3 
1 5 4 2 3 7 8 9 6 
3 6 9 8 4 5 7 2 1 
2 8 7 1 6 9 5 3 4 
5 2 1 9 7 4 3 6 8 
4 3 8 5 2 6 9 1 7 
7 9 6 3 1 8 4 5 2295种解法
解法1:
8 1 2 7 5 3 6 4 9 
9 4 3 6 8 2 1 7 5 
6 7 5 4 9 1 2 8 3 
1 5 4 2 3 7 8 9 6 
3 6 9 8 4 5 7 2 1 
2 8 7 1 6 9 5 3 4 
5 2 1 9 7 4 3 6 8 
4 3 8 5 2 6 9 1 7 
7 9 6 3 1 8 4 5 2 
解法2:
8 1 2 7 5 3 6 4 9 
9 4 3 6 8 2 1 7 5 
6 7 5 4 9 1 2 8 3 
1 5 4 2 3 7 8 9 6 
3 6 9 8 4 5 7 2 1 
2 8 7 1 6 9 5 3 4 
5 2 1 9 7 4 3 6 8 
4 3 8 5 2 6 9 1 7 
7 9 6 3 1 8 4 5 2 
解法3:
...

总结

通过使用回溯算法,我们可以有效地求解数独问题。虽然回溯算法在最坏情况下的时间复杂度较高,但对于标准9x9的数独问题,它通常能够在合理的时间内找到解决方案。希望本文对你理解数独求解算法有所帮助,并激发你进一步探索算法的兴趣。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/33658.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为hcia——Datacom实验指南——TCP传输原理和数据段格式

什么是TCP TCP是一种可靠的端到端的传输层协议&#xff0c;仅应用于单波通信。 采用TCP协议作为传输方式的应用层服务&#xff0c;再进行数据传输前&#xff0c;都需要进行TCP协议的创建。 TCP报文的格式 sequence number&#xff08;序列号&#xff09; 占4个字节&#x…

Vlog 片头制作

打开剪映&#xff0c;新建草稿&#xff0c;导入黑色背景。 拉长时间轴&#xff0c;背景时常调整为4.2秒。 添加文本&#xff0c;输入 5 个“|”&#xff0c;每个中间 2 个空格&#xff0c;如下| | | | |&#xff0c;然后手动放大文本&#xff0c;让中间显示出四个间隔。 继续添…

【Nacos】服务发布之优雅预热上线方案

目录 一、背景二、注册时机2.1、注册机制2.2、分析源码找到注册时机 三、注册前心跳健康检测3.1、方案实施3.2、源码分析3.3、优化代码 四、流量权重配置五、总结5.1、整体完整流程&#xff1a;5.2、流程图&#xff1a;5.1、优化方案完整代码&#xff1a; 一、背景 有些面向广…

VXLAN 组播 RP

一、Anycast RP 在每个 VTEP 上&#xff0c;每个多播组都会建立一个源树 (S,G)&#xff0c;并且在双活 Leaf 设备上到 RP 地址是 ECMP 路径。 在 PIM ASM 模式下&#xff0c;(S,G) 组在 VTEP 端创建。由于每个 VTEP 都能够为特定的多播组发送和接收多播流量&#xff0c;因此每…

【第七节】windows sdk编程:Windows 中的对话框

目录 引言 一、对话框简介 1.1 对话框的创建 1.2 基本函数 1.3 模态对话框与非模态对话框 1.4 对话框与窗口的区别 二、模态对话框编程方法 2.1 模态对话框编程 2.2 消息框 三、非模态对话框编程方法 四、综合代码案例 引言 在Windows应用程序开发中&#xff0c;对话…

安装并配置终端字体

1. 简介 在使用 Oh My Zsh Powerlevel10k 时&#xff0c;正确的字体配置至关重要。Powerlevel10k 依赖 Nerd Fonts 扩展字体&#xff0c;以正确显示 Git 状态、分支、时间、图标等信息。 如果没有正确配置字体&#xff0c;你可能会看到 乱码、问号&#xff08;?&#xff09…

LeetCode - #227 基于 Swift 实现基本计算器

摘要 在这篇文章中&#xff0c;我们将实现一个基于 Swift 语言的基本计算器。该计算器能够解析和计算包含 、-、* 和 / 的数学表达式&#xff0c;并且遵循运算符的优先级规则。整数除法仅保留整数部分&#xff0c;不能使用 eval() 这样的内置解析方法。 描述 给你一个字符串表…

智慧应急消防解决方案(35页PPT)(文末有下载方式)

详细资料请看本解读文章的最后内容。在当今社会&#xff0c;消防安全至关重要&#xff0c;关乎人民生命财产安全和社会稳定。随着科技的飞速发展&#xff0c;智慧应急消防解决方案应运而生&#xff0c;为消防工作带来了新的变革和机遇。接下来&#xff0c;让我们深入探讨这份智…

网络安全反渗透 网络安全攻防渗透

网络渗透防范主要从两个方面来进行防范&#xff0c;一方面是从思想意识上进行防范&#xff0c;另一方面就是从技术方面来进行防范。 1.从思想意识上防范渗透 网络攻击与网络安全防御是正反两个方面&#xff0c;纵观容易出现网络安全事故或者事件的公司和个人&#xff0c;在这些…

2025-03-15 学习记录--C/C++-PTA 练习3-4 统计字符

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 练习3-4 统计字符 本题要求编写程序&#xff0c;输入10个字符&#xff0c;统计其中英文字母、空格或回车、…

11a-PPDU

## 前导码和信令 OFDM 物理层&#xff08;PHY&#xff09;的 PPDU&#xff08;物理层协议数据单元&#xff09;格式包含以下实体信息&#xff1a; - **PPDU 组成**&#xff1a;由 OFDM PHY preamble&#xff08;前导码&#xff0c;12 个符号&#xff09;、PHY header&#xff…

TF-IDF:文本挖掘中的关键词提取利器

引言 在自然语言处理&#xff08;NLP&#xff09;和文本挖掘中&#xff0c;TF-IDF是一种常用的技术&#xff0c;用于评估一个词在文档中的重要性。它不仅在信息检索领域广泛应用&#xff0c;还在文本分类、关键词提取等任务中发挥着重要作用。本文将详细介绍TF-IDF的原理…

[新能源]新能源汽车快充与慢充说明

接口示意图 慢充接口为交流充电口&#xff08;七孔&#xff09;&#xff0c;快充接口为直流充电口&#xff08;九孔&#xff09;。 引脚说明 上图给的是充电口的引脚图&#xff0c;充电枪的为镜像的。 慢充接口引脚说明 快充接口引脚说明 充电流程 慢充示意图 慢充&…

docker3-容器与镜像命令

前言 容器命令[部分] docker run –name“nginx-lb” 这个就是为容器起一个名称 以前是随机起的名称 docker run -d --name mynginx1 nginx:1.24.0 docker ps 这样就可以看到我们起的名字了 docker stop mynginx1 这个就可以停掉指定名字的容器了&#xff0c;但不是删除…

vue/react/vite前端项目打包的时候加上时间最简单版本,防止后端扯皮

如果你是vite项目&#xff0c;直接写一个vite的插件&#xff0c;通过这个插件可以动态注入环境变量&#xff0c;然后当打包的时候&#xff0c;自动注入这个时间到环境变量中&#xff0c;然后在项目中App.vue中或者Main.tsx中打印出来&#xff0c;这就知道是什么时候编译的项目了…

Linux中Gdb调试工具常用指令大全

1.gdb的安装 如果你是root用户直接用指令 &#xff1a;yum install gdb &#xff1b;如果你是普通用户用指令&#xff1a;sudo yum install gdb&#xff1b; 2.gdb调试前可以对你的makefile文件进行编写&#xff1a; 下面展示为11.c文件编写的makefile文件&#xff1a; code…

go 安装swagger

1、依赖安装&#xff1a; # 安装 swag 命令行工具 go install github.com/swaggo/swag/cmd/swaglatest# 安装 gin-swagger 和 swagger 文件的依赖 go get -u github.com/swaggo/gin-swagger go get -u github.com/swaggo/files 2、测试 cmd中输入&#xff1a; swag -v 如果…

数据库---sqlite3

数据库&#xff1a; 数据库文件与普通文件区别: 1.普通文件对数据管理(增删改查)效率低 2.数据库对数据管理效率高,使用方便 常用数据库: 1.关系型数据库: 将复杂的数据结构简化为二维表格形式 大型:Oracle、DB2 中型:MySql、SQLServer …

go的gmp

参考链接&#xff1a;https://www.bilibili.com/video/BV19r4y1w7Nx Golang的GMP调度模型(协程调度器)是其并发编程的核心。GMP代表Goroutine、Machine和Processor三个关键组成部分。Goroutine是Go语言中的轻量级线程&#xff0c;Machine是操作系统的线程&#xff0c;Processor…

标贝自动化数据标注平台推动AI数据训练革新

随着人工智能&#xff08;AI&#xff09;技术的快速发展&#xff0c;数据标注作为AI模型训练的关键环节&#xff0c;其重要性日益凸显。传统的人工数据标注方式虽然能够提供高质量的标注数据&#xff0c;但存在效率低、成本高、一致性差等问题。为了解决这些问题&#xff0c;标…