目录

什么是深度优先搜索(DFS)

DFS题型分类

DFS和回溯的关系

排列与组合

LeetCode 46 全排列

LeetCode 47 全排列 II

LeetCode 39 组合总和

LeetCode 40 组合总和 II

子集

LeetCode 78 子集

LeetCode 90 子集 II

分割问题

LeetCode 131 分割回文串

LeetCode 22 括号生成

图搜索类

LeetCode 79 单词搜索

LeetCode 200 岛屿数量


什么是深度优先搜索(DFS)

深度优先搜索(Depth-First Search,简称 DFS)是一种遍历或搜索图、树等数据结构的算法。其基本思想是尽可能深的搜索图的分支,当不能继续深度搜索时,回溯到上一个分支点,继续搜索其它未被访问过的路径。

DFS 的核心思想是通过递归来实现对每个节点的深度遍历。

DFS的基本原理
1. 起始点:从根节点(或图中的某个节点)开始
2. 访问节点:访问当前节点,并将其标记为已访问
3. 递归或栈操作:从当前节点的未访问邻接节点出发,递归地进行 DFS 搜索
4. 回溯:如果当前节点的所有邻接节点都已访问,则回溯到上一个节点,继续对其它未访问的节点进行 DFS 搜索

DFS的应用场景
图的遍历:DFS常用于在图中遍历所有节点和边,可以用于解决很多图算法问题,比如找连通分量、拓扑排序、环检测。
树的遍历:在树结构中,DFS通过先访问子节点再访问父节点,可以找到树的所有路径,比如路径和问题、最小/最大深度等。
回溯问题:DFS的一个重要应用是回溯问题,比如全排列、组合问题、数独问题,它通过递归的方式尝试所有可能的选择,直到找到符合条件的解。

DFS的实现方式
1. 递归实现:通过递归函数调用来模拟栈的行为。
2. 非递归实现:使用栈(或队列)显式地保存每一个待访问的节点。

递归实现
递归版 DFS 的流程简洁清晰,通过递归函数来实现深度优先的遍历。

void DFS(TreeNode* node) {if (node == nullptr) return;// 访问当前节点cout << node->val << " ";// 遍历左子树DFS(node->left);// 遍历右子树DFS(node->right);
}

非递归实现(栈版)
非递归版 DFS 使用栈显式地保存当前节点及其未访问的邻接节点。

void DFS(TreeNode* root) {stack<TreeNode*> s;if (root) s.push(root);while (!s.empty()) {TreeNode* node = s.top();s.pop();cout << node->val << " ";// 注意顺序:先右后左,因为栈是后进先出if (node->right) s.push(node->right);if (node->left) s.push(node->left);}
}

由于栈是后进先出,所以需要先把右子节点压入栈,才能确保左子节点在栈顶,优先被访问。

DFS的特点
空间复杂度:DFS 在递归实现中通常需要 O(h) 的栈空间,其中 h 为树的高度,最坏情况下是 O(n),当树为链状结构时。
时间复杂度:对于图的 DFS,时间复杂度是 O(V + E),其中 V 为节点数,E 为边数。对于树的 DFS,时间复杂度是 O(n),其中 n 为节点数。

DFS题型分类

排列与组合类:
全排列:生成所有可能的排列
组合问题:从一个集合中选择多个元素(有时可以重复选择,有时不能)
常见题目:
LeetCode 46(全排列)
LeetCode 47(全排列 II)
LeetCode 39(组合总和)
LeetCode 40(组合总和 II)

子集类:
生成一个集合的所有子集,通常是二进制枚举的变种
常见题目:
LeetCode 78(子集)
LeetCode 90(子集 II)

分割问题:
将一个集合或数组划分成几个小部分或进行某种分割,通常需要满足特定条件
常见题目:
LeetCode 131(分割回文串)
LeetCode 22(括号生成)

图搜索类:
主要在二维网格或图结构中进行搜索,寻找特定的路径或模式
常见题目:
LeetCode 79(单词搜索)
LeetCode 200(岛屿数量)

数独与拼图类:
解决类似数独之类的填充或排列问题,使用回溯法逐步尝试填充,发现无解时撤回
常见题目:
LeetCode 37(数独解法)

DFS和回溯的关系

回溯法是一种算法思想,DFS 是实现回溯的常用手段。
DFS 的核心:是一种遍历或搜索策略,沿着一条路径尽可能深入地探索,直到无法继续再回溯到上一个节点,选择另一条路径继续探索。
回溯法的核心:是在搜索过程中,当发现当前路径不可能得到有效解时,主动放弃该路径(剪枝),回退到上一步重新选择,以避免无效搜索。
关系:
回溯法通常使用 DFS 来实现,但也可以用 BFS 等其他搜索方式
DFS 强调遍历的过程,回溯强调 "试错 - 回退 - 再试" 的决策过程
回溯法在 DFS 的基础上增加了剪枝操作,提高搜索效率

排列与组合

排列与组合问题是回溯算法中最基础的类型,它们通常涉及如何从一个集合中选择或排列元素

1. 排列问题:
排列指的是从一组元素中选取出所有可能的排列,排列中元素的顺序非常重要。
要求生成给定集合中所有元素的不同排列。
特点:
需要考虑元素的顺序,排列的结果与选取的元素顺序相关。
如果题目中有重复元素,需要进行去重处理,避免重复的排列。
排列问题可以通过递归(回溯)实现,逐步将元素放入不同的位置,尝试所有可能的排列。

LeetCode 46 全排列

解题思路:
回溯法核心思想:

每次选择一个元素:将数组中的每个元素都尝试放入不同的位置。
递归生成剩余的排列:当选定一个元素放入当前排列后,递归生成剩下的元素的排列。
回溯:当递归深入到最深层(即排列完成),就需要回溯到上一步,去尝试不同的元素组合。
去重:题目中已经保证了输入数组没有重复元素,所以不需要处理去重问题。

递归树的构建:
对于数组 [1, 2, 3],第一层递归会选择 1、2、3 中的一个元素放入排列中。然后,递归地继续选择剩余的元素,直到数组中的元素都被选完为止。

vector<vector<int>> permute(vector<int>& nums) {vector<vector<int>> res;  // 存储所有排列结果vector<int> path;  // 存储当前路径(排列)dfs(nums, path, res);return res;  
}void dfs(vector<int>& nums, vector<int>& path, vector<vector<int>>& res) {if (path.size() == nums.size()) {res.push_back(path);return;}for (int i = 0; i < nums.size(); ++i) {// 检查当前元素是否已在路径中if (find(path.begin(), path.end(), nums[i]) != path.end()) {continue;}path.push_back(nums[i]);  // 选择当前元素// 递归调用,继续构建路径dfs(nums, path, res);path.pop_back();  // 回溯,移除最后一个元素}
}

if (find(path.begin(), path.end(), nums[i]) != path.end()):

这个条件通过 find 函数检查 nums[i] 是否已经出现在当前排列 path 中

find 函数会返回一个迭代器,如果元素在 path 中找到了,返回的迭代器位置将不等于 path.end()。

如果 nums[i] 已经在 path 中,则跳过本次循环,继续检查下一个元素。这是为了避免重复的元素出现在排列中。

时间复杂度是 O(n!),因为生成全排列需要遍历所有可能的排列,排列的总数为 n!
空间复杂度是 O(n),因为递归的深度最多为 n,且每次递归都需要存储当前的排列。

LeetCode 47 全排列 II
vector<vector<int>> permuteUnique(vector<int>& nums) {vector<vector<int>> res;vector<int> path;vector<bool> used(nums.size(), false);  // 标记元素是否被使用过sort(nums.begin(), nums.end());  // 排序使得相同元素相邻dfs(nums, used, path, res);return res;  
}void dfs(vector<int>& nums, vector<bool>& used, vector<int>& path, vector<vector<int>>& res) {if (path.size() == nums.size()) {res.push_back(path);return;}for (int i = 0; i < nums.size(); ++i) {if (used[i]) {continue;}// 去重关键:当前元素与前一个元素相同,且前一个元素未被使用时跳过if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {continue;}used[i] = true;path.push_back(nums[i]);dfs(nums, used, path, res);path.pop_back();used[i] = false;  }
}

sort(nums.begin(), nums.end()) 的作用是让相同的数字相邻,这样在回溯时才能用 nums[i] == nums[i-1] 判断重复并正确剪枝。如果不排序,相同的数字可能分散在数组各处,无法正确去重,从而生成重复的排列。
例如 nums = [1, 2, 1]:
当 i=2(nums[2] = 1),nums[i-1] = 2,nums[i] != nums[i-1],所以不会跳过,导致重复选择 1

find(path.begin(), path.end(), nums[i]) 这种方法不能正确处理重复数字的排列问题
去重必须保证相同数字的使用顺序
如果 nums[i] == nums[i-1],并且 nums[i-1] 还没被使用(!used[i-1]),说明 nums[i] 是重复的,应该跳过。这样才能保证相同数字按顺序被选中,避免重复排列。

find 无法实现这一点,find 只检查 nums[i] 是否在 current 中,但无法判断它是否是应该跳过的重复数字

第一层递归:
  i=0:选 nums[0]=1 → 进入子树
  i=1:发现 nums[1]=1 且 nums[0] 未被使用 → 跳过(剪枝)
  i=2:选 nums[2]=2 → 进入子树

如果 nums[i] == nums[i-1] 且 !used[i-1],说明 nums[i-1] 还没被选,但 nums[i] 却先被选了,这样会导致重复,所以跳过 nums[i]。
如果 used[i-1] == true,说明 nums[i-1] 已经被选了,nums[i] 可以正常选。

2. 组合问题:
组合指的是从一组元素中选取若干个元素,顺序不重要。
要求从给定集合中选取元素,不需要考虑顺序,输出所有的组合。
特点:
组合问题不关心顺序,只关心选取了哪些元素。
选取的元素数量可以固定,也可以可变。
如果有重复元素,通常需要进行去重处理。
组合问题的回溯通常是选择或不选择的决策过程,逐步选择元素并递归地生成组合。

LeetCode 39 组合总和

sort 函数把 candidates 数组从小到大排好。有两个好处:
方便剪枝:如果某枚硬币比剩下要凑的钱还多,后面更大的硬币肯定也凑不上,直接跳过
避免重复组合:通过控制只能从当前硬币及后面的硬币里选,避免[2,3] 和 [3,2] 这种重复情况
可以把这个题想象成凑硬币

dfs 参数介绍

参数名含义例子(目标 7,当前选了 2)
a排序后的硬币数组(不能改)[2,3,6,7]
start从哪枚硬币开始选(控制不重复)0(第一次选)、0(选了 2 后还能再选 2)
remain还需要凑多少钱才能到目标7(刚开始)、5(选了 2 后)
path当前正在尝试的组合(能改,选硬币就加,不选就删)[2](刚选了第一个 2)
res最终要返回的所有有效组合(能改,找到一个就加进去)刚开始是空的,后来加 [2,2,3]
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {sort(candidates.begin(), candidates.end()); vector<vector<int>> res;vector<int> path;dfs(candidates, 0, target, path, res);return res;
}void dfs(const vector<int>& a, int start, int remain,vector<int>& path, vector<vector<int>>& res) {if (remain == 0) {               res.push_back(path);return;}for (int i = start; i < a.size(); ++i) {if (a[i] > remain) break;     // 剪枝path.push_back(a[i]);        dfs(a, i, remain - a[i], path, res); // 传 i 允许重复选path.pop_back();              // 撤销选择}
}

LeetCode 40 组合总和 II

代码和组合总和有两处不同:
1. dfs递归时传的是i + 1(而不是i),意思是下一次只能从当前元素的下一个开始选(避免重复使用同一个元素)
2. 多了一行if (i > start && a[i] == a[i - 1]) continue; 作用是去掉同一层循环中重复的元素(避免出现重复组合)为什么i > start?因为i == start时是第一个出现的元素,需要保留

vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {sort(candidates.begin(), candidates.end());vector<vector<int>> res;vector<int> path;dfs(candidates, 0, target, path, res);return res;
}void dfs(const vector<int>& a, int start, int remain,vector<int>& path, vector<vector<int>>& res) {if (remain == 0) {res.push_back(path);return;}for (int i = start; i < a.size(); ++i) {if (a[i] > remain) break;                         // 剪枝if (i > start && a[i] == a[i - 1]) continue;      // 同层去重path.push_back(a[i]);dfs(a, i + 1, remain - a[i], path, res);          path.pop_back();}
}

子集

子集类题型的特点
1. 目标是生成所有子集(幂集)

给定一个数组,子集类问题的核心就是:对于每个元素,都有两种选择,即要或不要
所以总共有 2^n 种子集(包括空集和全集)

2. 顺序不重要
子集问题与排列不同,元素的顺序不影响子集的唯一性
例如 [1,2] 和 [2,1] 被认为是同一个子集
所以子集类问题的递归搜索常使用 start 参数,避免回头

3. 两种常见实现方式
回溯法(DFS):递归过程中,把当前路径视为一个子集,依次探索「选择 / 不选择某个元素」的分支
二进制枚举:用一个二进制数表示某个元素是否被选入子集。例如 nums = [1,2,3],101 表示子集 [1,3]

4. 是否包含重复元素 → 处理方式不同
无重复元素:直接回溯或枚举即可,典型题目是 LeetCode 78 子集
有重复元素:要做去重,和组合 II(LeetCode 40)的思路类似,需要排序+同层去重,典型题目是 LeetCode 90 子集 II

LeetCode 78 子集

方法一:DFS

vector<vector<int>> subsets(vector<int>& nums) {vector<vector<int>> res;vector<int> path;dfs(nums, 0, path, res);return res;
}void dfs(const vector<int>& nums, int start,vector<int>& path, vector<vector<int>>& res) {res.push_back(path);for (int i = start; i < nums.size(); ++i) {path.push_back(nums[i]);          dfs(nums, i + 1, path, res);      path.pop_back();                   }
}

方法二:二进制枚举

vector<vector<int>> subsets(vector<int>& nums) {int n = nums.size();vector<vector<int>> res;for (int mask = 0; mask < (1 << n); ++mask) {vector<int> path;for (int i = 0; i < n; ++i) {if (mask & (1 << i)) {path.push_back(nums[i]); // 该位为1则选}}res.push_back(move(path));}return res;
}

1 << n 是个位运算,意思是 1 向左移 n 位,等价于 2^n

mask & (1 << i) 可以判断 mask 的第 i 位是否为 1

move() 作用是将一个对象转换为右值引用
带 move 时:
path 中的数据直接被转移到 res 中,两者首元素地址相同(没有新内存分配)
转移后 path 变成空向量(资源已被取走)
不带 move 时:
res 中会生成新的拷贝,首元素地址和原 path 不同
path 仍然保留原有数据(内存未释放)

左值(Lvalue):能放在赋值号左边的持久对象
本质:有明确的内存地址,且生命周期较长(比如函数内的局部变量、全局变量、数组元素等),可以被多次访问或修改。

右值(Rvalue):只能放在赋值号右边的临时对象
本质:没有持久的内存身份,要么是临时生成的数值,要么是即将被销毁的对象,生命周期很短。

LeetCode 90 子集 II
vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(), nums.end());        // 排序便于同层去重vector<vector<int>> res;vector<int> path;dfs(nums, 0, path, res);return res;
}void dfs(const vector<int>& nums, int start,vector<int>& path, vector<vector<int>>& res) {res.push_back(path);for (int i = start; i < (int)nums.size(); ++i) {// 同层去重:本层已用过 nums[i-1] 作为起点,就不要再用相同值的 nums[i]if (i > start && nums[i] == nums[i - 1]) continue;path.push_back(nums[i]);dfs(nums, i + 1, path, res);     path.pop_back();}
}

同层去重:本层已用过 nums[i-1] 作为起点,就不要再用相同值的 nums[i]

分割问题

分割类问题的核心:在某些位置切或不切,形成搜索树
解法通常是 回溯 + 剪枝,加上条件判断
难点在于:
如何快速判断切出来的部分是否合法(回文判定/括号合法等)
如何避免重复计算(可以用 DP 优化)

LeetCode 131 分割回文串
vector<vector<string>> partition(string s) {int n = s.size();vector<vector<string>> res;vector<string> path;// 预处理回文表vector<vector<bool>> isPal(n, vector<bool>(n, false));for (int i = n - 1; i >= 0; --i) {for (int j = i; j < n; ++j) {if (s[i] == s[j] && (j - i < 2 || isPal[i + 1][j - 1])) {isPal[i][j] = true;}}}// 回溯切分dfs(s, 0, isPal, path, res);return res;
}void dfs(const string& s, int start,const vector<vector<bool>>& isPal,vector<string>& path,vector<vector<string>>& res) {if (start == s.size()) {     res.push_back(path);return;}for (int end = start; end < s.size(); ++end) {if (!isPal[start][end]) continue;           // 剪枝path.push_back(s.substr(start, end - start + 1));dfs(s, end + 1, isPal, path, res);        path.pop_back();                             }
}

dfs参数:
s:原始字符串
start:当前拆分的起始位置(从这里开始找下一个回文子串)
isPal:前面算好的回文表
path:当前正在拆分的方案
res:存储所有有效的拆分方案

以 s = "aab" 为例:

第一次调用 dfs:start=0(从开头开始拆),path 为空
循环 end 从 0 开始:
end=0:isPal[0][0]是 true("a" 是回文)→ path 变成 ["a"] → 调用 dfs (start=1)
第二次调用 dfs:start=1(从索引 1 开始拆),path=["a"]
循环 end 从 1 开始:
end=1:isPal[1][1]是 true("a" 是回文)→ path 变成 ["a","a"] → 调用 dfs (start=2)
第三次调用 dfs:start=2(从索引 2 开始拆),path=["a","a"]
循环 end=2:isPal[2][2]是 true("b" 是回文)→ path 变成 ["a","a","b"] → 调用 dfs (start=3)
start=3 等于字符串长度 3 → 方案 ["a","a","b"] 加入 res
回溯后继续尝试:
回到 start=1 时,end=2:isPal[1][2]是 false("ab" 不是回文)→ 跳过
回到 start=0 时,end=1:isPal[0][1]是 true("aa" 是回文)→ path 变成 ["aa"] → 调用 dfs (start=2)
start=2 时选 "b" → path 变成 ["aa","b"] → 加入 res

最终 res 里就有了两种方案:[["a","a","b"], ["aa","b"]]

LeetCode 22 括号生成

思路:
只能在还没用完左括号时放 (:open < n
只能在当前序列仍然合法时放 ):close < open
终止:open == n && close == n,收集结果

vector<string> generateParenthesis(int n) {vector<string> res;string path;dfs(n, 0, 0, path, res);return res;
}void dfs(int n, int open, int close, string& path, vector<string>& res) {if (open == n && close == n) {   res.push_back(path);return;}if (open < n) {                  // 还能放左括号path.push_back('(');dfs(n, open + 1, close, path, res);path.pop_back();}if (close < open) {              // 右括号数量必须 < 左括号path.push_back(')');dfs(n, open, close + 1, path, res);path.pop_back();}
}

dfs参数:
n:需要生成的括号对数
open:当前已经用了多少个左括号 (
close:当前已经用了多少个右括号 )
path:当前正在构建的括号串
res:存储所有有效的括号组合

图搜索类

题目一般给一个 二维矩阵(grid)、迷宫地图或者图(Graph),要求搜索是否存在路径、找出所有路径、或者计算最短路径。

常用搜索方式
DFS(深度优先搜索)
沿着一个方向不断深入,直到走不通再回溯
适合:判断连通性、找路径、求面积、遍历所有可能
BFS(广度优先搜索)
从起点出发,一层层向外扩展
适合:求最短路径(层数 = 步数)

常用技巧
访问标记(visited):避免重复走已经访问过的点,防止无限循环
方向数组(dx, dy):简化对上下左右的遍历

int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};

问题模式
是否存在路径:DFS 或 BFS,典型如单词搜索(79)
连通块计数:DFS/BFS 遍历整张图,典型如岛屿数量(200)
最短路径:BFS(层数就是最短步数),典型如最短路径 in 二维网格、迷宫问题
复杂约束:结合回溯,典型如数独求解(37)

复杂度特点
DFS/BFS 都是 O(V+E)(顶点+边),二维网格里一般是 O(m·n)
回溯题型则会指数爆炸,但剪枝能优化

LeetCode 79 单词搜索

方法一:使用 visited 数组

bool exist(vector<vector<char>>& board, string word) {int m = board.size(), n = board[0].size();vector<vector<int>> vis(m, vector<int>(n, 0));// 找可能的起点for (int i = 0; i < m; ++i) {for (int j = 0; j < n; ++j) {if (board[i][j] == word[0] && dfs(board, word, i, j, 0, vis))return true;}}return false;
}const int dx[4] = {1, -1, 0, 0};
const int dy[4] = {0, 0, 1, -1};bool dfs(vector<vector<char>>& b, const string& w,int x, int y, int k, vector<vector<int>>& vis) {if (k == w.size()) return true;    // 终止条件int m = b.size(), n = b[0].size();// 剪枝if (x < 0 || x >= m || y < 0 || y >= n) return false;if (vis[x][y] || b[x][y] != w[k]) return false;vis[x][y] = 1;   // 标记// 向四个方向探索(下、上、右、左)for (int dir = 0; dir < 4; ++dir) {int nx = x + dx[dir], ny = y + dy[dir];if (dfs(b, w, nx, ny, k + 1, vis)) return true;}// 四个方向都走不通,进行回溯vis[x][y] = 0;                                        return false;
}

dfs参数:
b:网格
w:要找的单词
x,y:当前所在的网格坐标
k:当前要匹配的单词字符索引(比如 k=0 表示要匹配 word [0])
vis:标记已使用单元格的数组

方法二:原地标记(省内存)

用一个特殊字符(#)暂存覆盖,以表示已访问,回溯时再还原

bool exist(vector<vector<char>>& board, string word) {int m = board.size(), n = board[0].size();for (int i = 0; i < m; ++i) {for (int j = 0; j < n; ++j) {if (dfs(board, word, i, j, 0)) return true;}}return false;
}const int dx[4] = {1, -1, 0, 0};
const int dy[4] = {0, 0, 1, -1};bool dfs(vector<vector<char>>& b, const string& w, int x, int y, int k) {if (k == w.size()) return true;int m = b.size(), n = b[0].size();if (x < 0 || x >= m || y < 0 || y >= n) return false;if (b[x][y] != w[k]) return false;char keep = b[x][y];b[x][y] = '#';                // 原地标记为已用for (int dir = 0; dir < 4; ++dir) {int nx = x + dx[dir], ny = y + dy[dir];if (dfs(b, w, nx, ny, k + 1)) {b[x][y] = keep;        // 回溯前还原return true;}}b[x][y] = keep;            // 还原return false;
}

LeetCode 200 岛屿数量

思路:发现一个岛,就清空一个岛
遍历整个网格,只要遇到一个没被访问过的陆地('1'),就说明发现了一个新岛屿;
然后用 DFS 把这个岛屿上所有相连的陆地都 “变成水”(标记为 '0'),避免重复统计;
最后统计这样的 “新岛屿” 有多少个。

int numIslands(vector<vector<char>>& grid) {int m = grid.size();int n = grid[0].size();int ans = 0;for (int i = 0; i < m; ++i) {for (int j = 0; j < n; ++j) {if (grid[i][j] == '1') {   // 发现新岛屿++ans;dfs(grid, i, j);     // 淹没岛屿}}}return ans;
}void dfs(vector<vector<char>>& g, int x, int y) {int m = g.size(), n = g[0].size();if (x < 0 || x >= m || y < 0 || y >= n || g[x][y] != '1') return;g[x][y] = '0'; // 标记为已访问const int dx[4] = {1, -1, 0, 0};const int dy[4] = {0, 0, 1, -1};for (int d = 0; d < 4; ++d) {dfs(g, x + dx[d], y + dy[d]);}
}

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

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

相关文章

大模型备案、算法备案补贴政策汇总【广东地区】

广州海珠 《广州市海珠区建设人工智能大模型应用示范区实施细则的通知》规定&#xff0c;自 2024 年 6 月 18 日起至 2027 年 3 月 20 日&#xff0c;大规模企业首次完成国家级生成式人工智能&#xff08;大语言模型&#xff09;上线备案的&#xff0c;可获得最高 100 万元一次…

鸿蒙服务端开发资料汇总

文章目录鸿蒙服务端开发资料汇总一、核心概念1.1 分布式架构1.2 微内核设计1.3 元服务架构二、技术栈2.1 开发语言2.2 开发框架与工具2.3 核心技术能力三、官方文档与资源3.1 官方文档3.2 示例代码与开源资源四、应用案例4.1 政务领域4.2 金融领域4.3 交通出行4.4 企业办公五、…

基于51单片机霍尔测速仪表测转速调速系统设计

1 系统功能介绍 本设计为 基于51单片机霍尔测速仪表测转速调速系统。系统以STC89C52单片机为核心&#xff0c;结合霍尔传感器、LCD1602显示模块、电位器调速电路与电机驱动模块&#xff0c;实现了对旋转装置的转速检测、数据显示以及实时调节电机转速的功能。 系统主要功能包括…

前端-初识Vue实例

一.准备容器 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </head>…

基于FPGA的简易医疗呼叫器实现,包含testbench

目录 1.课题概述 2.系统测试效果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 医院护理呼叫系统是病人请求值班医生或护士进行紧急处理或咨询的工具&#xff0c;可将病人的请求快速传送给值班医生或护士。其基本功能就是通过一种简便的途径使护理对象与医…

Maya绑定基础:创建骨骼和蒙皮、蒙皮权重控制的两种方法

目录 1 创建骨骼和蒙皮 2 蒙皮权重控制 方法一 3 蒙皮权重控制 方法二 1 创建骨骼和蒙皮 新建一个圆柱体 把圆柱体拉长一点&#xff0c;调到前视图 骨架--创建关节 同时选中骨骼和模型&#xff0c;菜单栏--蒙皮--绑定蒙皮 对关节进行旋转 详细的步骤参考这个链接&#xf…

【Settings】OTA 升级时更新 Settings 数据库

一、问题描述 基于 Android 14平台&#xff0c;随着后续的 UI 更新需要将某个控制项的 Settings 值更新&#xff0c;需要更新 SettingsProvider 的值。二、问题分析 1. 定义一个 Settings 常量 frameworks/base/core/java/android/provider/Settings.java /*** Whether to use …

开发electron时候Chromium 报 Not allowed to load local resource → 空白页。

报错含义 主进程&#xff08;main.js&#xff09;里 win.loadFile()/win.loadURL() 指向了 file:///…/app.asar/build/index.html&#xff0c; 但打包后真正的文件位于 app.asar 内部&#xff0c;路径拼错&#xff0c;于是 Chromium 报 Not allowed to load local resource →…

Ethan独立开发新品速递 | 2025-08-30

每日精选Product Hunt上最值得关注的独立产品&#xff0c;发现全球创新灵感&#xff0c;助力你的产品成长。 下面从今天的 Product Hunt 热榜中筛选出对独立开发者&#xff08;尤其是注重开发工具、AI 工具、SaaS 与创业工具&#xff09;的10个最有参考价值的项目。筛选标准侧重…

【C++ 】string类操作全解析

1. 为什么学习 string 类&#xff1f; 1.1 C 语言中的字符串 C 语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C 标准库中提供了一些 str 系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合 OOP &…

DAY15-新世纪DL(DeepLearning/深度学习)战士:破(超参数调试、Batch正则化和程序框架)3

本文参考文章0.0 目录-深度学习第一课《神经网络与深度学习》-Stanford吴恩达教授-CSDN博客 1.调试处理 神经网络的改变会涉及到许多不同的超参数设置&#xff0c;现在&#xff0c;对于超参数而言&#xff0c;如何找到一套比较好的设定&#xff1f; 训练深度最难的事之一是你…

Android 14 PMS源码分析

源码参考&#xff1a;Search (aospxref.com) 一、简介 PackageManagerService&#xff08;简称 PMS&#xff09;&#xff0c;是 Android 系统核心服务之一&#xff0c;处理包管理相关的工作&#xff0c;常见的比如安装、卸载应用等。本章针对SyetemServer、PMS构造方法重点模…

内省排序:相对最迅速的通用排序算法

&#x1f50d; 内省排序&#xff1a;相对最迅速的通用排序算法 &#x1f680; 前言&#xff1a;排序算法的演进之路 排序算法是计算机科学的核心基础之一&#xff0c;其性能直接影响着数据库系统、科学计算、图形渲染等领域的效率。随着硬件架构的发展&#xff0c;排序算法经历…

Linux驱动开发重要操作汇总

本文主要记录imx6ull的linux驱动开发过程中常用的一些操作。 uboot编译 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- distclean make ARCHarm CROSS_COMPILEarm-linux-gnueabihf mx6ull_14x14_evk_emmc_defconfig make V1 ARCHarm CROSS_COMPILEarm-linux-gnueabihf- …

【Java后端】MySQL 常见 SQL 语句优化指南

在 MySQL 中&#xff0c;SQL 优化是性能调优的核心环节&#xff0c;尤其是在数据量大、并发高的情况下。这里整理一份 MySQL 常见 SQL 语句优化指南&#xff0c;从查询写法、索引使用到执行计划分析&#xff0c;涵盖实用技巧&#xff1a;1. 查询语句层面的优化 ✅ 避免 SELECT …

Golang 面试题「高级」

以下是 100 道 Golang 高级面试题及答案&#xff0c;聚焦语言底层实现、并发深度优化、性能调优、源码级理解等核心方向&#xff0c;适合资深开发者或架构师级别的面试场景&#xff1a; 一、GPM 调度模型与并发深度 问题&#xff1a;Goroutine 的栈空间初始大小是多少&#xff…

WebGIS视角:体感温度实证,哪座“火炉”火力全开?

目录 前言 一、火炉城市空间分布及特点 1、空间分布 2、气候特点 二、数据来源及技术实现 1、数据来源介绍 2、技术路线简介 三、WebGIS系统实现 1、后端设计与实现 2、前端程序实现 四、成果展示 1、整体展示 2、蒸烤模式城市 3、舒适城市 五、总结 前言 “火炉…

《数据结构入门:顺序表的结构设计与核心操作(C 语言版)》

目录 一. 线性表 二. 顺序表的概念与结构 2.1 核心概念 2.2 两种常见结构 静态顺序表 动态顺序表 2.3 核心区别对比 四. 顺序表的实现 4.1 顺序表的定义 4.2 顺序表初始化 4.3 动态顺序表容量检查与扩容 4.4 动态顺序表插入数据 4.4.1 头插 4.4.2 尾插 4.4.3 指…

[Maven 基础课程]Maven 是什么

Maven 的官方网站&#xff1a;https://maven.apache.org/ 来自 Maven 官网的对于 Maven 是什么的描述&#xff1a; Apache Maven is a build tool for Java projects. Using a project object model (POM), Maven manages a project’s compilation, testing, and documentat…

【MATLAB例程】三维组合导航,滤波使用EKF,带严格的惯导推算、雅克比求解函数,图像对比滤波前后的速度、位置、姿态

文章目录程序介绍系统建模滤波框架仿真设置性能对比代码优点运行结果MATLAB源代码程序介绍 本程序实现了 三维状态量的扩展卡尔曼滤波&#xff08;EKF&#xff09;组合导航仿真&#xff0c;采用严格的15维误差状态模型&#xff0c;状态向量包括&#xff1a; x[pxpypzvxvyvzϕθ…