数据结构与算法之递归: LeetCode 37. 解数独 (Ts版)
解数独
- https://leetcode.cn/problems/sudoku-solver/description/
描述
- 编写一个程序,通过填充空格来解决数独问题
- 数独的解法需 遵循如下规则:
- 数字 1-9 在每一行只能出现一次
- 数字 1-9 在每一列只能出现一次
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
- 数独部分空格内已填入了数字,空白格用 ‘.’ 表示
示例 1
输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
提示
- board.length == 9
- board[i].length == 9
- board[i][j] 是一位数字或者 ‘.’
- 题目数据 保证 输入数独仅有一个解
Typescript 版算法实现
1 ) 方案1: 回溯
/**Do not return anything, modify board in-place instead.*/
function solveSudoku(board: string[][]): void {const line: boolean[][] = Array.from({ length: 9 }, () => Array(9).fill(false));const column: boolean[][] = Array.from({ length: 9 }, () => Array(9).fill(false));const block: boolean[][][] = Array.from({ length: 3 }, () => Array.from({ length: 3 }, () => Array(9).fill(false)));const spaces: number[][] = [];// 初始化状态for (let i = 0; i < 9; i++) {for (let j = 0; j < 9; j++) {if (board[i][j] === '.') {spaces.push([i, j]);} else {const digit = parseInt(board[i][j]) - 1;line[i][digit] = true;column[j][digit] = true;block[Math.floor(i / 3)][Math.floor(j / 3)][digit] = true;}}}// 深度优先搜索函数function dfs(pos: number): boolean {if (pos === spaces.length) {return true;}const [i, j] = spaces[pos];for (let digit = 0; digit < 9; digit++) {if (!line[i][digit] && !column[j][digit] && !block[Math.floor(i / 3)][Math.floor(j / 3)][digit]) {line[i][digit] = true;column[j][digit] = true;block[Math.floor(i / 3)][Math.floor(j / 3)][digit] = true;board[i][j] = (digit + 1).toString();if (dfs(pos + 1)) {return true;}// 回溯line[i][digit] = false;column[j][digit] = false;block[Math.floor(i / 3)][Math.floor(j / 3)][digit] = false;board[i][j] = '.';}}return false;}dfs(0);
}
2 ) 方案2: 位运算优化
/**Do not return anything, modify board in-place instead.*/
function solveSudoku(board: string[][]): void {const line: number[] = Array(9).fill(0);const column: number[] = Array(9).fill(0);const block: number[][] = Array.from({ length: 3 }, () => Array(3).fill(0));const spaces: number[][] = [];// 翻转函数,用于设置或清除位function flip(i: number, j: number, digit: number) {const mask = 1 << digit;line[i] ^= mask;column[j] ^= mask;block[Math.floor(i / 3)][Math.floor(j / 3)] ^= mask;}// 初始化状态for (let i = 0; i < 9; i++) {for (let j = 0; j < 9; j++) {if (board[i][j] === '.') {spaces.push([i, j]);} else {const digit = parseInt(board[i][j]) - 1;flip(i, j, digit);}}}// 深度优先搜索函数function dfs(pos: number): boolean {if (pos === spaces.length) {return true;}const [i, j] = spaces[pos];let mask = 0x1ff & ~(line[i] | column[j] | block[Math.floor(i / 3)][Math.floor(j / 3)]);while (mask > 0) {const digit = Math.log2(mask & -mask); // 找到最右侧的 1 的位置flip(i, j, digit);board[i][j] = (digit + 1).toString();if (dfs(pos + 1)) {return true;}flip(i, j, digit);mask &= mask - 1; // 将最右侧的 1 置为 0}return false;}dfs(0);
}
3 ) 方案3: 枚举优化
/**Do not return anything, modify board in-place instead.*/
function solveSudoku(board: string[][]): void {const line: number[] = Array(9).fill(0);const column: number[] = Array(9).fill(0);const block: number[][] = Array.from({ length: 3 }, () => Array(3).fill(0));const spaces: number[][] = [];// 翻转函数,用于设置或清除位function flip(i: number, j: number, digit: number) {const mask = 1 << digit;line[i] ^= mask;column[j] ^= mask;block[Math.floor(i / 3)][Math.floor(j / 3)] ^= mask;}// 初始化已知数字的状态for (let i = 0; i < 9; i++) {for (let j = 0; j < 9; j++) {if (board[i][j] !== '.') {const digit = parseInt(board[i][j]) - 1;flip(i, j, digit);}}}// 尝试通过唯一解填充空格let modified: boolean;do {modified = false;for (let i = 0; i < 9; i++) {for (let j = 0; j < 9; j++) {if (board[i][j] !== '.') {continue;}let mask = 0x1ff & ~(line[i] | column[j] | block[Math.floor(i / 3)][Math.floor(j / 3)]);if ((mask & (mask - 1)) === 0 && mask !== 0) { // mask 的二进制表示仅有一个 1const digit = Math.log2(mask); // 找到最右侧的 1 的位置flip(i, j, digit);board[i][j] = (digit + 1).toString();modified = true;}}}} while (modified);// 记录剩余未填充的空格for (let i = 0; i < 9; i++) {for (let j = 0; j < 9; j++) {if (board[i][j] === '.') {spaces.push([i, j]);}}}// 深度优先搜索函数function dfs(pos: number): boolean {if (pos === spaces.length) {return true;}const [i, j] = spaces[pos];let mask = 0x1ff & ~(line[i] | column[j] | block[Math.floor(i / 3)][Math.floor(j / 3)]);while (mask > 0) {const digit = Math.log2(mask & -mask); // 找到最右侧的 1 的位置flip(i, j, digit);board[i][j] = (digit + 1).toString();if (dfs(pos + 1)) {return true;}flip(i, j, digit);mask &= mask - 1; // 将最右侧的 1 置为 0}return false;}dfs(0);
}
相关文章:
数据结构与算法之递归: LeetCode 37. 解数独 (Ts版)
解数独 https://leetcode.cn/problems/sudoku-solver/description/ 描述 编写一个程序,通过填充空格来解决数独问题数独的解法需 遵循如下规则: 数字 1-9 在每一行只能出现一次数字 1-9 在每一列只能出现一次数字 1-9 在每一个以粗实线分隔的 3x3 宫内…...
【氮化镓】香港科技大学陈Kevin-单片集成GaN比较器
一、引言(Introduction) GaN HEMT的重要性 文章开篇便强调了氮化镓(GaN)高电子迁移率晶体管(HEMT)在下一代功率转换系统中的巨大潜力。GaN HEMT具备高开关频率、低导通电阻、高击穿电压以及宽工作温度范围等优势,使其成为功率电子领域的热门研究对象。这些特性使得GaN…...
axios的使用总结
一、Axios 简介 Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js。在 Vue 项目中,它主要用于发送 HTTP 请求来获取数据(如从 API 获取数据)或者提交数据(如用户登录、注册等表单数据)。 二…...
革新未来:高效智能数字人技术引领多元化应用
随着科技的不断进步,数字人技术已逐渐成为企业数字化转型中的重要工具。数字人不仅能够优化客户体验,还可以显著提升企业运营效率。本文将详细介绍一种高性能、高质量、低延迟、快速响应以及安全稳定的数字人技术方案,帮助企业在多元化场景中…...
使用批处理文件清除系统垃圾
第一步:打开记事本,里面的命令如下 echo off echo 正在清理临时文件,请稍候...:: 清理系统临时文件 echo 清理系统临时文件... del /q /f /s "%TEMP%\*.*" del /q /f /s "%WINDIR%\Temp\*.*" rd /s /q "%WINDIR%\T…...
总结5..
#include<stdio.h> struct nb {//结构体列队 int x, y;//x为横坐标,y为纵坐标 int s, f;//s为步数,//f为方向 }link[850100]; int n, m, x, y, p, q, f; int hard 1, tail 1; int a[52][52], b[52][52], book[52][52][91]; int main() { …...
Java 在包管理与模块化中的优势:与其他开发语言的比较
在开发复杂的、规模庞大的软件系统时,包管理和模块化设计起着至关重要的作用。它们不仅决定了代码的组织和可维护性,还直接影响到团队协作效率、扩展性和性能。在众多编程语言中,Java 凭借其成熟的生态系统、强类型系统和标准化的包管理机制&…...
LLMs(大型语言模型)的多智能体:Auto-GPT
LLMs(大型语言模型)的多智能体:Auto-GPT 是指在一个系统中集成多个具有不同能力、角色和任务的智能体,这些智能体能够相互协作、沟通和交互,以共同完成复杂的任务或解决复杂的问题。每个智能体都可以被视为一个独立的实体,具有自己的策略、目标和知识库,通过相互之间的…...
CPU狂飙900%如何分析?怎么定位?怎么溯源处理
当你的服务器CPU飙升到900%,系统卡顿、响应迟缓、业务受阻,这种令人焦虑的场景是否让你束手无策?别慌,这并不是世界末日,只要掌握正确的分析与定位方法,就能快速找到问题根源,并有效解决。 CPU…...
Excel 技巧17 - 如何计算倒计时,并添加该倒计时的数据条(★)
本文讲如何计算倒计时,并添加该倒计时的数据条。 1,如何计算倒计时 这里也要用公式 D3 - TODAY() 显示为下面这个样子的 然后右键该单元格,选 设置单元格格式 然后点 常规 这样就能显示出还书倒计时的日数了。 下拉适用到其他单元格。 2&a…...
Java中的阻塞队列--以LinkedBlockingQueue为例
顾名思义,就是一种在对队列进行出队或者入队操作的时候会阻塞的队列。下面使用JDK17中的LinkedBlockingQuece进行简单的介绍。 LinkedBlockingQueue基本结构 LinkedBlockingQueue的主要结构以及构成的数据结构如下图所示。具体来说包括 存储元素的链表࿰…...
16.5万煤气柜柜位计故障分析
一、事故经过: 2015年8月14日20点45分,16.5万立煤气柜柜顶油封溢流口有大量油液溢出,此时雷达柜位计在计算机上示值为63.79米,由于接近傍晚天色较暗,岗位操作员并未及时发现这一异常状况。22点45分左右&…...
高效沟通驱动LabVIEW项目成功
在LabVIEW项目开发中,由于涉及软件、硬件及多方协作,项目沟通效率的高低直接影响开发进度与最终质量。不明确的需求、信息传递中的误解以及跨部门协作的阻碍,常导致项目延误甚至失败。因此,建立高效的沟通机制,确保信息…...
大模型之三十三- 开源Melo 语音合成
大模型之三十三- 开源Melo 语音合成 文本到语音(TTS)系统从基于基础音素的模型演变成复杂的端到端神经方法,这种方法可以直接将文本转换为语音。这一变革得益于深度学习的进步和计算能力的提升,已经在语音的自然度、韵律控制和跨语言能力方面取得了重大进展 。现代TTS系统…...
论文复现:四轮转向车辆后轮转角控制方法研究
写在前面,主要参考以下这篇文章,并复现了其中几种后轮转角控制方法。 一、什么是四轮转向 顾名思义,四轮转向指的是四个轮子都能转向,都能转动。当驾驶员操作方向盘进行前轮转向时,后轮按照特定算法给出的转角跟着转动…...
【UFEN】基于多层特征融合和多任务学习的多模态情感分析
abstract 当前多模态情感分析面临的主要挑战包括:1、模型如何在单一模态中提取情感信息,并实现多模态信息的互补传输;2、在单一模态中体现的情绪与多模态标签不一致的情况下,如何输出相对稳定的预测;3、当单模态信息不…...
uniapp的插件开发发布指南
Hbuilder创建项目 项目根目录创建uni_modules 开发组件 发布到插件市场 填写发布说明(未登录需要登录) 点击提交 在终端可以看到 发布成功! 插件市场查看...
【Linux系统】—— 编译器 gcc/g++ 的使用
【Linux系统】—— 编译器 gcc/g 的使用 1 用 gcc 直接编译2 翻译环境2.1 预处理(进行宏替换)2.2 编译(生成汇编)2.3 汇编(生成机器可识别代码)2.4 链接2.5 记忆小技巧2.6 编译方式2.7 几个问题2.7.1 如何理…...
[微服务]注册中心优化
环境隔离 企业实际开发中,往往会搭建多个运行环境,例如: 开发环境测试环境预发布环境生产环境 这些不同环境之间的服务和数据之间需要隔离。 还有的企业中,会开发多个项目,共享nacos集群。此时,这些项目…...
C++ ——— 模拟实现 vector 类
目录 vector 类的框架 无参数的构造函数 析构函数 获取有效数据个数 获取容量 重载 [] 运算符 可读可写版本 只可读版本 扩容 尾插 实现迭代器 可读可写版本 只可读版本 自定义设置size长度和内容 在任意位置插入 删除任意位置的数据 赋值重载 vector 类的框…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
