回溯法理论基础 LeetCode 77. 组合 LeetCode 216.组合总和III LeetCode 17.电话号码的字母组合
目录
回溯法理论基础
回溯法
回溯法的效率
用回溯法解决的问题
如何理解回溯法
回溯法模板
LeetCode 77. 组合
回溯算法的剪枝操作
LeetCode 216.组合总和III
LeetCode 17.电话号码的字母组合
回溯法理论基础
回溯法
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。回溯一般与递归相辅相成,并在递归后进行使用。回溯法是一种暴力搜索方法,你可以想象你现在在走迷宫,当你走到一个死路后,是否需要回退,而回退的这个过程就是回溯。
回溯法的效率
正如上面所说,回溯法是暴力搜索方法,其本质是穷举,穷举所有可能,然后选出我们想要的答案。如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。
用回溯法解决的问题
那么既然回溯法并不高效为什么还要用它呢? 因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。
那什么问题适合用回溯法进行解决?
- 组合问题:N个数里面按一定规则找出k个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 棋盘问题:N皇后,解数独等等
需要特别说明下组合和排列的问题,组合是没有顺序的,排列是有顺序,比如一对男女朋友,这是一个组合,但你问到他们之间谁先表白,这就变成排列了。
如何理解回溯法
回溯法解决的问题都可以抽象为树形结构。
因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度。有递归,就必须要有终止条件,所以必然是一棵高度有限的树(N叉树)。
回溯法模板
回溯法也有模板,在将其模版前,复习下递归的模板。
- 终止条件
- 递归顺序
- 输入参数和输出参数
回溯法模板。
1.回溯函数终止条件
既然回溯可以抽象为树形结构,那么也像递归二叉树那样存在终止条件。什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。
回溯函数终止条件伪代码如下:
if (终止条件) {存放结果;return;
}
2.回溯搜索的遍历过程
在上面我们提到了,回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。
上图是特意举例集合大小和孩子的数量是相等的。size为4的集合有4个子集合。
回溯函数遍历过程伪代码如下:
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果
}
for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。
backtracking这里自己调用自己,实现递归。
大家可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。
3.回溯函数模板返回值以及参数
回溯算法中函数返回值一般为void。
回溯算法需要的参数可不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数。
回溯函数伪代码如下:
void backtracking(参数)
回溯算法模板框架如下:
void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
}
涉及回溯算法的题都可以基于上述模板进行实现,回溯算法模板 + 题目特性 → 解决回溯算法问题
LeetCode 77. 组合
为什么要用回溯算法,你用两个for循环也可以做到,第一个循环从左到右逐个遍历,第二个从第一个循环的下标的下一个位置开始遍历,这样的话也可以做到,时间复杂度是O(),那如果是k越大呢?此时不就需要更多个循环了吗,那此时时间复杂度就是
,显然时间复杂度太高。
回溯方法的做法其实就是用递归来替代循环。
思路:
- 通过for循环横向遍历起始处理节点(红箭头)
- 通过递归深度遍历可能出现的组合(蓝箭头)
- 递归后进行回溯
- 当组合长度 == k 时,为终止条件
手撕Code
class Solution(object):def combine(self, n, k):""":type n: int:type k: int:rtype: List[List[int]]"""self.result = [] ## 二维数组self.path = []self.backtracking(n, k, 1)return self.resultdef backtracking(self, n, k, start_index):"""n 记录需要处理到的最大数字k 记录需要输出的数组数目start_index 记录当前处理的数字, 遍历范围是1到n"""### 终止条件if len(self.path) == k:# self.result.append(self.path) ### 这里是将指针给append了,self.path的内容是一直在变的,如果你将指针append的话,上一轮的递归的结果并没有进行保存,因为上一轮指向的数组已经变了。self.result.append(list(self.path))return### 递归逻辑for _ in range(start_index, n+1): ### 左闭右开self.path.append(start_index) ### 当前处理节点。处理同一层的逻辑self.backtracking(n, k, start_index + 1) ### 递归,往深度遍历。self.path.pop() ### 递归后进行回溯start_index += 1 ### 处理完当前节点后,处理下一个节点
易错点:
不能是self.result.append(self.path) ,这部分append只是当前self.path指针所指向的当前数组元素,如果self.path当前指向的数组是[1,3],上一轮self.result已经将[1,2]给存进去了,此时如果你要将[1,3]数组进行append,你应该append是这个数组,而不应该是这个指针,即你想要的结果是[ [1,2], [1,3] ],如果你是append这个指针的话,只会添加当前指针指向的数组,因为你存储的其实是指向当前数组的指针,即[ self.path, self.path ],再进行下一轮的append操作后,其实你获取的是[ [1,3], [1,3] ]。指针指向的是不断在变的数组,如果你存指针的话,最终的self.result只会是存储操作完毕后该指针下的元素,没有记录整个过程。
总结:当你在终止条件 if len(self.path) == k: 中执行 self.result.append(self.path) 时,你并没有将 self.path 当前的内容复制一份,而是将 self.path 这个列表对象本身的引用添加到了 self.result 中。
回溯算法的剪枝操作
体现在for循环范围的限制。如还是组合问题,现在k变了,变为4。
那么我们可以看到由于元素只有4个,因此只有从元素1开始的遍历才能得到最终符合数目为k的组合,而其他子树的遍历是不需要的,因为肯定数目是不符合的。那如果k是3呢?是不是只有从1开始和从2开始进行遍历才是有效的,且其子遍历中,有些分支也可以进行去掉,从而减少遍历的操作,这是与通过多个循环暴力搜索法相比所体现出的优势。
那要如何优化以实现剪枝呢?其实答案就体现在for循环范围的控制。优化过程如下:
i = start_index,表示处理节点的起始位置。
- 已经选择的元素个数:path.size();
- 所需需要的元素个数为: k - path.size();
- 列表中剩余元素(n-i) >= 所需需要的元素个数(k - path.size())
- 在集合n中至多要从该起始位置 : i <= n - (k - path.size()) + 1,开始遍历。
根据3得到的是 i <= n - (k - path.size()) ,为什么要+1?因为包括起始位置,我们要是一个左闭的集合。
这个优化条件 i <= n - (k - path.size()) + 1
实际上是在说: "当前的起始位置 i
必须小于或等于这样一个值:从这个值开始,到最大的元素 n
结束,能够恰好凑齐我们所需要的 k - path.size()
个元素,并且还考虑到起始位置 i
本身也算一个元素。"
n-i 是 指包含起始位置之后的列表元素个数,当n-i = k - path.size()时,此时有临界条件,i = n - (k - path.size),这表示i为这个值时,当前元素+后续元素刚好是符合
k - path.size()的。但我们要的临界条件是i到了什么值时,后续遍历操作可以不用执行,因此要加1,1是表明i = n - (k - path.size)时,后序的元素可以进行遍历,再往后的就不用了。
举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。表示最大的符合条件在当第二个元素开始时。因此,从2开始。
class Solution(object):def combine(self, n, k):""":type n: int:type k: int:rtype: List[List[int]]"""self.result = [] ## 二维数组self.path = []self.backtracking(n, k, 1)return self.resultdef backtracking(self, n, k, start_index):"""n 记录需要处理到的最大数字k 记录需要输出的数组数目start_index 记录当前处理的数字, 遍历范围是1到n"""### 终止条件if len(self.path) == k:# self.result.append(self.path) ### 这里是将指针给append了,self.path的内容是一直在变的,如果你将指针append的话,上一轮的递归的结果并没有进行保存,因为上一轮指向的数组已经变了。self.result.append(list(self.path))returnfor _ in range(start_index, (n -(k-len(self.path))+1) +1): ### 剪枝操作self.path.append(start_index) self.backtracking(n, k, start_index + 1) self.path.pop() start_index += 1
LeetCode 216.组合总和III
思路
- 与LeetCode组合类似,只不过这里是求和,判断是否有满足数目的path == sum
- 思路基本一致,注意循环中不要忘了对start_index进行修改。
手撕Code
class Solution(object):def combinationSum3(self, k, n):""":type k: int:type n: int:rtype: List[List[int]]"""self.result = []self.path = []self.nums = []for i in range(1,10):self.nums.append(i) ### 创建一个数组用于存储用于遍历的数字self.backtracking(0, k, n)return self.resultdef backtracking(self, start_index, k, n):if len(self.path) == k:sum = 0for i in range(len(self.path)):sum += self.path[i]if sum == n:self.result.append(list(self.path))returnfor _ in range(start_index, len(self.nums)-(k-len(self.path)) +1):self.path.append(self.nums[start_index])self.backtracking(start_index+1, k, n)self.path.pop()start_index += 1
LeetCode 17.电话号码的字母组合
思路1 —— 朴实无华
- 分级处理思想,之前操作是都是在同一个数组内,现在是多个数组,那只要修改回溯函数对应的输入和处理方法就行。具体地,输入两个数组,第一个数组进行横向遍历,第二个数据进行遍历。为什么在一个集合里使用start_index,是为了实现去重操作;在多个集合的操作,多个集合无交集的情况下,直接从0开始遍历,不用考虑去重操作。
- 针对k=2时,传入两个数组就可以直接处理。而针对k=3,需要将k=2时的处理结果拿出来后,作为新的数组,与第三个数组输入到回溯函数中,从而实现k=3的计算逻辑。
- k=4也是类似,就是逐步将前面的输出结果作为新的输入与下一个进行计算。
上述这种思路,跟用循环进行实现没什么区别了,没体现回溯的特点。另外,在处理数字为1的时候要注意,字符串不是像数组那般可以进行修改,因此当遇到1时,直接跳过就行,可以用一个new_digits来存储对旧digits进行判断修改后的结果。
class Solution(object):def letterCombinations(self, digits):""":type digits: str:rtype: List[str]"""### 采用一个Hashmap存储对应数字和字母的关系### 对输入的数字进行判断已获得其对应的字母hash_map = dict()hash_map[2] = ['a','b','c']hash_map[3] = ['d','e','f']hash_map[4] = ['g','h','i']hash_map[5] = ['j','k','l']hash_map[6] = ['m','n','o']hash_map[7] = ['p','q','r', 's']hash_map[8] = ['t','u','v']hash_map[9] = ['w','x','y', 'z']### 构造一个函数,第一个数组作为横向遍历,第二个数组作为纵向遍历self.result = []self.path = [] k = len(digits)# left = right = 0 #### python中的字符串是无法直接修改的,因此碰到1的话,应该是选择跳过,而不是进行修改# while right < k: # if digits[left] != '1':# left += 1# right += 1# elif digits[left] == '1':# right += 1# digits[left] = digits[right] cur = 0digits_list = []while cur < k:if digits[cur] != '1':digits_list.append(digits[cur])cur += 1new_digits = ''.join(digits_list)k = len(new_digits)if k == 0:return self.resultif k == 1:return hash_map[int(digits[0])]first_str = hash_map[int(digits[0])]second_str = hash_map[int(digits[1])]if k == 2:self.backtracking(0, first_str, second_str, k)if k == 3:self.backtracking(0, first_str, second_str, 2)first_second_str = list(self.result)self.result = []third_str = hash_map[int(digits[2])]self.backtracking(0, first_second_str, third_str, 2)if k == 4:self.backtracking(0, first_str, second_str, 2)first_second_str = list(self.result)self.result = []third_str = hash_map[int(digits[2])]first_str = self.backtracking(0, first_second_str, third_str, 2)first_second_third_str = list(self.result)self.result = []forth_str = hash_map[int(digits[3])]self.backtracking(0, first_second_third_str, forth_str, 2)return self.resultdef backtracking(self, start_index, first_str, second_str, k):"""first_str : 第一个字符串数组,横向遍历second_str : 第二个字符串数组,深度遍历k : path的长度 == digits.length"""if len(self.path) == k:path_str = ''.join(self.path)self.result.append(path_str)returnfor _ in range(start_index, len(first_str)):self.path.append(first_str[start_index])self.backtracking(0, second_str, None, k)self.path.pop()start_index += 1
思路2:
- 关键还是在对回溯的理解,回溯的结构可以抽象成一颗树,在单个集合中,你是将单个元素分到树的每一层中。现在是多个集合,其实也就是把单个集合分到树的每一层中。
- 在了解上述思想后,我们就可以设计index了,index表示的是digits的长度,其长度决定了有几个集合,有N个集合就决定了有N-1个回溯递归的深度(因为第一个是横向遍历)。
Code
class Solution(object):def letterCombinations(self, digits):""":type digits: str:rtype: List[str]"""self.letterMap = [ ### 一个一维数组,数字的大小刚好对应去在数组中去获得该数字对应字符串的下标"", # 0"", # 1"abc", # 2"def", # 3"ghi", # 4"jkl", # 5"mno", # 6"pqrs", # 7"tuv", # 8"wxyz" # 9]self.result = []self.path = []new_digits = []for dig in digits: ### 判断数字是否在[2,9]之内,如果有不存在的需要进行删除if dig != '1' or dig != '0':new_digits.append(dig)new_digits = "".join(new_digits)length = len(new_digits) ### 数字的长度index = 0 ### 树的深度if length == 0:return self.resultself.backtracking(index, new_digits, length)return self.resultdef backtracking(self, index, new_digits, length):if index == length:self.result.append("".join(list(self.path)))return ### 终止条件cur = int(new_digits[index]) ### 当前处理的数字cur_str = self.letterMap[cur] ### 当前处理的数字 对应的字符串for i in range (len(cur_str)):self.path.append(cur_str[i])self.backtracking(index+1, new_digits, length)self.path.pop()# index += 1
注意,在self.path.pop()之后,index不需要+1,与start_index进行区分开来。start_index + 1是为了获得一个数组中后续的元素。而在不同的集合中,你index是操作不同集合,index为0传进后是第一层,如何往下进行深度遍历操作,已经在index+1作为输入参数进行输入时已经确定了,后序递归结束时,重新执行for循环,进行横向遍历,再一步递归去深度遍历。
相关文章:
回溯法理论基础 LeetCode 77. 组合 LeetCode 216.组合总和III LeetCode 17.电话号码的字母组合
目录 回溯法理论基础 回溯法 回溯法的效率 用回溯法解决的问题 如何理解回溯法 回溯法模板 LeetCode 77. 组合 回溯算法的剪枝操作 LeetCode 216.组合总和III LeetCode 17.电话号码的字母组合 回溯法理论基础 回溯法 回溯法也可以叫做回溯搜索法,它是一…...
LeetCode --- 156双周赛
题目列表 3541. 找到频率最高的元音和辅音 3542. 将所有元素变为 0 的最少操作次数 3543. K 条边路径的最大边权和 3544. 子树反转和 一、找到频率最高的元音和辅音 分别统计元音和辅音的出现次数最大值,然后相加即可,代码如下 // C class Solution {…...
第五项修炼:打造学习型组织
最近一直接到的需求,都是公司董事长或总经理都特别推崇《第五项修炼:打造学习型组织》的内容,让各个层级的管理者都持续学习、应用、实践。我不禁开始反思,这背后到底隐藏着什么原因? 随着商业环境的变化和复杂性的增加…...
Bellman - Ford 算法与 SPFA 算法求解最短路径问题 ——从零开始的图论讲解(4)
目录 前言 为什么Dijkstra算法面对负权值图会有误差??? 举例说明 什么是Bellman -Ford算法? BF算法的核心思想 什么是松弛 为什么最多松弛N-1次? 代码实现 举例 初始状态(dist[] 数组) 第 1 轮松弛(遍历所有边) …...
Python训练营打卡 Day27
函数专题2:装饰器 知识点回顾: 装饰器的思想:进一步复用函数的装饰器写法注意内部函数的返回值 昨天我们接触到了函数大部分的功能,然后在你日常ctrl点进某个复杂的项目,发现函数上方有一个xxx,它就是装饰器 装饰器本质…...
初识计算机网络。计算机网络基本概念,分类,性能指标
初识计算机网络。计算机网络基本概念,分类,性能指标 本系列博客源自作者在大二期末复习计算机网络时所记录笔记,看的视频资料是B站湖科大教书匠的计算机网络微课堂,祝愿大家期末都能考一个好成绩! 视频链接地址 一、…...
5月16日day27打卡
函数专题2:装饰器 知识点回顾: 装饰器的思想:进一步复用函数的装饰器写法注意内部函数的返回值 作业: 编写一个装饰器 logger,在函数执行前后打印日志信息(如函数名、参数、返回值) logger def …...
【生成式AI文本生成实战】DeepSeek系列应用深度解析
目录 🌟 前言🏗️ 技术背景与价值🩹 当前技术痛点🛠️ 解决方案概述👥 目标读者说明 🧠 一、技术原理剖析📊 核心概念图解💡 核心作用讲解🔧 关键技术模块说明⚖️ 技术选…...
【Pandas】pandas DataFrame kurt
Pandas2.2 DataFrame Computations descriptive stats 方法描述DataFrame.abs()用于返回 DataFrame 中每个元素的绝对值DataFrame.all([axis, bool_only, skipna])用于判断 DataFrame 中是否所有元素在指定轴上都为 TrueDataFrame.any(*[, axis, bool_only, skipna])用于判断…...
2025年渗透测试面试题总结-安恒[实习]安全服务工程师(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 安恒[实习]安全服务工程师 1. SQLMap爆出当前库名的参数是什么? 2. Nmap探测系统的参数&am…...
在 Visual Studio Code (VSCode) 中配置 MCP(Model Context Protocol)
前提条件 安装 VSCode:确保已安装最新版本的 VSCode(建议使用 1.99 或以上版本,支持 MCP)。安装 GitHub Copilot 扩展:MCP 通常与 GitHub Copilot 的代理模式(Agent Mode)结合使用,…...
顶层架构 - 消息集群推送方案
一、推送基础概念简述 在即时通讯(IM)系统中,最基础的一件事就是“如何把消息推送给用户”。为了实现这个过程,我们要先了解两种常见的网络通信方式:HTTP 和 WebSocket。 1. HTTP 是什么? HTTP 就像一次性…...
C++性能测试工具——Vtune等的介绍
一、介绍 我们在前面的相关文章中对C性能的测试和分析工具(见“C性能测试工具gprof和gperftools基础”等)有一个初步的了解和应用,其实类似的相关工具还有不少。为了进一步的让开发者们掌握更多的相关性能测试分析相关的方法,对另…...
车道线检测----CLRKDNet
今天的最后一篇 车道线检测系列结束 CLRKDNet:通过知识蒸馏加速车道检测 摘要:道路车道是智能车辆视觉感知系统的重要组成部分,在安全导航中发挥着关键作用。在车道检测任务中,平衡精度与实时性能至关重要,但现有方法…...
【AI模型部署】
解决python引入huggingface_hub模块下载超时问题 背景问题解决 背景 AMD Ryzen™ AI处理器通过独特的NPUGPU异构架构,为AI工作负载提供强大的并行计算能力。本方案展示了如何将YOLOv8目标检测、RCAN超分辨率重建和Stable Diffusion文生图三类模型分别部署到NPU和GP…...
排序01:多目标模型
用户-笔记的交互 对于每篇笔记,系统记录曝光次数、点击次数、点赞次数、收藏次数、转发次数。 点击率点击次数/曝光次数 点赞率点赞次数/点击次数 收藏率收藏次数/点击次数 转发率转发次数/点击次数 转发是相对较少的,但是非常重要,例如转发…...
电子电器架构 --- Zonal架构正在开创汽车电子设计新时代
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...
如何阅读、学习 Tcc (Tiny C Compiler) 源代码?如何解析 Tcc 源代码?
阅读和解析 TCC(Tiny C Compiler) 的源代码需要对编译器的基本工作原理和代码结构有一定的了解。以下是分步骤的指南,帮助你更高效地学习和理解 TCC 的源代码: 1. 前置知识准备 C 语言基础:TCC 是用 C 语言编写的&…...
Java 泛型与类型擦除:为什么解析对象时能保留泛型信息?
引言:泛型的“魔术”与类型擦除的困境 在 Java 中,泛型为开发者提供了类型安全的集合操作,但其背后的**类型擦除(Type Erasure)**机制却常常让人困惑。你是否遇到过这样的场景? List<String> list …...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(22):复习
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(22):复习 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)复习(2)復習3、单词(1)日语(2)日语片假名单词4、对话练习5、单词辨析记录6、总结1、前言 (1)情况说明 自己在今年,在日本留学中,目前在语言学校,…...
Java基础学习
Java 基础大纲 1. Java 概述 Java 语言特点(跨平台、面向对象、自动内存管理) JVM、JRE、JDK 的作用与区别 开发环境搭建(安装 JDK、配置环境变量、IDE 使用) 2. 基础语法(已经学习) 变量与数据类型&a…...
MGX:多智能体管理开发流程
MGX的多智能体团队如何通过专家混合系统采用全新方法,彻底改变开发流程,与当前的单一智能体工具截然不同。 Lovable和Cursor在自动化我们的特定开发流程方面取得了巨大飞跃,但问题是它们仅解决软件开发的单一领域。 这就是MGX(MetaGPT X)的用武之地,它是一种正在重新定…...
2025第三届盘古石杯初赛(计算机部分)
前言 比赛的时候时间不对,打一会干一会,导致比赛时候思路都跟不上,赛后简单复现一下,希望大家批批一下 计算机取证 1、分析贾韦码计算机检材,计算机系统Build版本为?【标准格式:19000】 183…...
XML介绍及常用c及c++库
一.xml概述 1.什么是XML? XML(eXtensible Markup Language)是一种标记语言,1998 年 2 月:XML 1.0 发布,用于存储和传输结构化数据。与HTML专注于数据显示不同,XML专注于数据本身及其结构。 它…...
动态规划-63.不同路径II-力扣(LeetCode)
一、题目解析 与62.不同路径不同的一点是现在网格中有了障碍物,其他的并没有什么不同 二、算法解析 1.状态表示 dp[i][j]表示:到[i,j]位置时,不同的路径数 2.状态转移方程 由于多了障碍物,所以我们要判断是否遇到障碍物 3.初…...
海盗王3.0的数据库3合1并库处理方案
原版的海盗王数据库有3个accountserver,gamedb,tradedb,对应到是账号数据库,游戏数据库,商城数据库。 一直都有个想法,如何把这3个库合并到一起,这样可以实现一些功能。 涉及到sqlserver的数据库…...
Vue百日学习计划Day16-18天详细计划-Gemini版
重要提示: 番茄时钟: 每个番茄钟为25分钟学习,之后休息5分钟。每完成4个番茄钟,进行一次15-30分钟的长休息。动手实践: DOM 操作和事件处理的理解高度依赖于实际编码。请务必在浏览器中创建 HTML 页面,并配…...
【C++】15.并发支持库
本篇内容参考自cplusplus 1. thread 1.1 thread thread库底层是对各个系统的线程库(Linux下的pthread库和Windows下Thread库)进行封装。C11thread库的第一个特点是可以跨平台,第二个特点是Linux和Windows下提供的线程库都是面向过程的&…...
Linux系统编程——exec族函数
我们来完整、系统、通俗地讲解 Linux 系统编程中非常重要的一类函数:exec 族函数(也叫 exec family)。 一、什么是 exec? exec 系列函数的作用是: 用一个新的程序,替换当前进程的内容。 也就是说…...
职教实训室中的写实数字人:技术与应用方案
在当今快速发展的数字化时代,职业教育的重要性日益凸显。面对传统教学模式中个性化不足、互动性差等挑战,深声科技基于2D写实交互数字人的解决方案为职教实训室带来了全新的变革。本文将详细介绍该技术方案的核心原理、产品特色及其在职业培训中的实际应…...
Nginx模块配置与请求处理详解
Nginx 作为模块化设计的 Web 服务器,其核心功能通过不同模块协同完成。以下是各模块的详细配置案例及数据流转解析: 一、核心模块配置案例 1. Handler 模块(内容生成) 功能:直接生成响应内容(如静态文件、重定向等) # 示例1:静态文件处理(ngx_http_static_module)…...
54. 螺旋矩阵
题目链接: a54. 螺旋矩阵 题目描述: 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 题目分析: 改题目需要判断是否溢出边界,与59不同,59可以判断是否为0…...
virtualbox虚拟机中的ubuntu 20.04.6安装新的linux内核5.4.293 | 并增加一个系统调用 | 证书问题如何解决
参考文章:linux添加系统调用【简单易懂】【含32位系统】【含64位系统】_64位 32位 系统调用-CSDN博客 安装新内核 1. 在火狐下载你需要的版本的linux内核压缩包 这里我因为在windows上面下载过,配置过共享文件夹,所以直接复制粘贴通过共享文…...
代码随想录算法训练营第三十八天打卡
今天是动态规划的第三天,昨天的不同路径与整数分解的几道题目大家理解得如何?如果有疑问大家还是多去想想dp数组究竟是什么含义,还有我的状态转移是否正确,初始化是否正确,这一点很重要,今天的题目依旧是跑…...
【论信息系统项目的整合管理】
论信息系统项目的整合管理 某省机场管理集团航空货运站原有物流生产信息系统无法满足机场货运站生产信息与航空公司、对方航站、进出口航空货物按海关监管要求电子报关等行业信息实时共享发展需要,生产信息需多次重复录入问题已成为业务发展最大瓶颈,急需…...
小学数学题批量生成及检查工具
软件介绍 今天给大家介绍一款近期发现的小工具,它非常实用。 软件特点与出题功能 这款软件体积小巧,不足两兆,具备强大的功能,能够轻松实现批量出题。使用时,只需打开软件,输入最大数和最小数,…...
Python线性回归:从理论到实践的完整指南
Python线性回归:从理论到实践的完整指南 线性回归是数据科学和机器学习中最基础且最重要的算法之一。本文将深入探讨如何使用Python实现线性回归,从理论基础到实际应用,帮助读者全面理解这一重要的统计学和机器学习方法。 什么是线性回归&a…...
python 爬虫框架介绍
文章目录 前言一、Requests BeautifulSoup(基础组合)二、Scrapy(高级框架)三、PySpider(可视化爬虫)四、Selenium(浏览器自动化)五、Playwright(新一代浏览器自动化&…...
强化学习算法实战:一个例子搞懂sarsa、dqn、ddqn、qac、a2c及其区别
简介 在学习强化学习算法:sarsa、dqn、ddqn、qac、a2c、trpo、ppo时,由于有大量数学公式的推导,觉得十分晦涩,且听过就忘记了。 但是当把算法应用于实战时,代码的实现要比数学推导直观很多。 接下来通过不同的算法实现…...
文章记单词 | 第86篇(六级)
一,单词释义 pretty /ˈprɪti/- adj. 漂亮的;相当的 /adv. 相当地labour /ˈleɪbə(r)/- n. 劳动;劳工;分娩 /v. 劳动;努力(英式英语, labor)imaginary /ɪˈmdʒɪnəri/- adj. …...
firewall防火墙
一.Firewalld 防火墙概述 1.firewalld 简介 firewalld 的作用是为包过滤机制提供匹配规则(或称为策略),通过各种不同的规则告诉netfilter 对来自指定源、前往指定目的或具有某些协议特征的数据包采取何种处理方式为了更加方便地组织和管理防火墙,firewa11d 提供了…...
TII-2024《AGP-Net: Adaptive Graph Prior Network for Image Denoising》
推荐深蓝学院的《深度神经网络加速:cuDNN 与 TensorRT》,课程面向就业,细致讲解CUDA运算的理论支撑与实践,学完可以系统化掌握CUDA基础编程知识以及TensorRT实战,并且能够利用GPU开发高性能、高并发的软件系统…...
Pageassist安装(ollama+deepseek-r1)
page-assist网站:https://github.com/n4ze3m/page-assist 首先电脑配置node.js,管理员打开命令窗口输入下面命令下载bun npm install -g buncd 到你想要安装page-assist的地方(推荐桌面) 输入下列命令 git clone https://gith…...
Java—— 方法引用 : :
方法引用是什么 把已经存在的方法拿过来用,当做函数式接口中抽象方法的方法体 方法引用符 :: 方法引用的条件 1.需要有函数式接口 2.被引用方法必须已经存在 3.被引用方法的形参和返回值需要跟抽象方法保持一致 4.被引用方法的功能要满足当前…...
Linux基础开发工具大全
目录 软件包管理器 1>软件包 2>软件生态 3>yum操作 a.查看软件包 b.安装软件 c.卸载软件 4>知识点 vim编辑器 1>基本概念 2>基本操作 3>正常模式命令集 a.模式切换 b.移动光标 c.删除 d.复制 e.替换 f.撤销 g.更改 4>底行模式命令…...
C语言实现INI配置文件读取和写入
一.INI文件介绍 INI配置文件是一种简单的文本文件,用于存储配置信息,通常由一个或多个节(section)组成,每个节包含多个键值对(Key-Value)格式。INI文件易于阅读和编辑,广泛应用于多…...
volatile关键字详解
volatile关键字详解 1. 定义与核心作用 volatile 是Java中的关键字,用于修饰变量,主要解决多线程环境下的内存可见性和指令重排序问题。其核心作用: 保证可见性:确保所有线程读取到变量的最新值。禁止指令重排序:防止…...
二叉树子树判断:从递归到迭代的全方位解析
一、题目解析 题目描述 给定两棵二叉树root和subRoot,判断root中是否存在一棵子树,其结构和节点值与subRoot完全相同。 示例说明 示例1: root [3,4,5,1,2],subRoot [4,1,2] 返回true,因为root的左子树与subRoot完…...
【PhysUnits】4.1 类型级比特位实现解释(boolean.rs)
一、源码 该代码实现了一个类型级(type-level)的布尔系统,允许在编译时进行布尔运算。 //! 类型级比特位实现 //! //! 这些是基础的比特位类型,作为本库中其他数值类型的构建基础 //! //! 已实现的**类型运算符**: //! //! - 来自 core::op…...
(7)python开发经验
文章目录 1 找不到资源文件2 使用subprocess执行时有黑色弹窗3 找不到exec4 pyside6-project lupdate的bug5 找不到pyd模块6 pyd模块编码错误7 运行显示Qt platform plugin "windows" in "8 tr()包含的字符串无法被翻译 更多精彩内容👉内容导航 &…...