回溯算法精讲

原理

回溯,就和深度优先遍历(DFS)类似,属于先一层到底直至到终点,如果这条路径不对,则回退一下,再继续往下搜索。

抽象地说,解决一个回溯问题,实际上就是遍历一棵决策树的过程,树的每个叶子节点存放着一个合法答案。你把整棵树遍历一遍,把叶子节点上的答案都收集起来,就能得到所有的合法答案。

基本策略属于:”深度优先遍历“ + ”状态回退“

核心框架

路径:已经做出的选择。
选择列表:当前可以做的选择。
结束条件:达到决策树底层,无法再做选择的条件。

for 属于横向遍历
递归,属于纵向遍历
这样就将整个集合全部搜索了一遍

在这里插入图片描述

void backtrack(路径, 选择列表):if 满足结束条件:result.add(路径)returnfor 选择 in 选择列表:做选择backtrack(路径, 选择列表)撤销选择

做选择:将当前的选择加入到路径中,并更新选择列表和路径。
递归调用:继续向下逐层递归,如果到达决策树的底层,满足结束条件,则将当前路径加入到结果集中。
撤销选择:递归完成返回后,撤销前一步的选择,恢复状态,尝试其他选项。

核心就是 for 循环里面的递归,在递归调用之前「做选择」,在递归调用之后「撤销选择」

回溯法主要解决什么问题?

当回溯法求解问题,我们可以尽量将问题抽象为树形结构, 上文我们都说过属于 深度遍历搜索 + 回退状态,所以尽可能的抽象为树结构。
回溯法就可以理解为,再集合中递归查询子集,集合大小就是抽象树的宽度,递归深度就是抽象树的深度。

组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等

组合问题

力扣77题:组合

在这里插入图片描述
思路:
第一,先抽象为一个树。
在这里插入图片描述
第二:找到终止条件
根据题意,当路径达到我们需要的,我们就直接终止

        if(path.size() == k){res.push_back(path);return;}

第三、怎么横向遍历?
直接往后一步一步挪动。

        for(int i = start; i<=n;i++){path.push_back(i);back(n,k,i+1);path.pop_back();}

该题解完整代码:

class Solution {
public:vector<vector<int>> res; // 最终结果vector<int> path;		//	每次查询的路径vector<vector<int>> combine(int n, int k) {back(n,k,1);// 测试输出for (vector<vector<int>>::iterator it = res.begin(); it != res.end(); it++) {for (vector<int>::iterator jt = (*it).begin(); jt != (*it).end(); jt++) {cout << *jt << " ";}cout << endl;}return res;}// 回溯代码void back(int n,int k ,int start){if(path.size() == k){res.push_back(path);return;}for(int i = start; i<=n;i++){path.push_back(i);back(n,k,i+1);path.pop_back();}}
};

如何优化呢?就是剪枝问题了。比如就这道题,如果 N= 4, k = 4,那是不是从 2 开始后面都不行,因为不够4个数。但是我们上文代码,属于全部搜索一下。如何优化?
在这里插入图片描述

for (int i = start; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置

力扣 216 组合总和III

在这里插入图片描述

这题和上题,思路基本一致,唯一修改,就是我们再遍历路径的时候,对路径上的值进行加减。 直接看代码:

class Solution {
public:vector<vector<int>> res;vector<int> path;int all = 0; // 用来记录路径上的值void back(int k ,int n, int start){if(all > n){  // 剪枝操作,此处代表后续路径都不可能会存在了return;}if(path.size()==k&& all == n){// if(all == n){//     res.push_back(path);// }res.push_back(path);return;}for(int i = start ; i<=9 - (k-path.size()) + 1 ;i++){all += i;   path.push_back(i);back(k,n,i+1);// int c = path.back();// path.pop_back();// all -= c;  // 回退也需要将他减去path.pop_back();all -= i;}}vector<vector<int>> combinationSum3(int k, int n) {back(k,n,1);return res;}
};

力扣17.电话号码的字母组合

在这里插入图片描述
思路:

这道题终究还是归结于组合问题,无非事每一层树节点需要根据数字进行判断,看下图。

在这里插入图片描述

class Solution {
public:const string letterMap[10] = {"", // 0"", // 1"abc", // 2"def", // 3"ghi", // 4"jkl", // 5"mno", // 6"pqrs", // 7"tuv", // 8"wxyz", // 9};vector<string> res;string path;void back(string digits ,int index){if(index == digits.size()){res.push_back(path);return;}int digit = digits[index] - '0';string letters = letterMap[digit];for(int i = 0 ; i< letters.size();i++){path.push_back(letters[i]); back(digits,index+1);  // 递归处理下一层数字path.pop_back();}}vector<string> letterCombinations(string digits) {if(digits.size() == 0){return res;}back(digits,0);return res;}
};

力扣39. 组合总和

在这里插入图片描述
思路:会故意想上文216题,是不是也是存在路径值得问题?但这题与之区别是什么呢?因为这题得节点可以重复选取,我们需要怎么改变?

看代码,我们在 back得时候,是不是又从 i 节点开始得?代表我每次都可以重复选取
在这里插入图片描述

        for(int i = index;i<candidates.size();i++){path.push_back(candidates[i]);num += candidates[i];back(candidates,target,i);num -= candidates[i];path.pop_back();}

完整代码:

class Solution {
public:vector<vector<int>> res;vector<int>path;int num = 0;void back(vector<int> candidates,int target,int index){if(num > target){return;}if(num == target){res.push_back(path);return;}for(int i = index;i<candidates.size();i++){path.push_back(candidates[i]);num += candidates[i];back(candidates,target,i);num -= candidates[i];path.pop_back();}}vector<vector<int>> combinationSum(vector<int>& candidates, int target) {for(int i = 0;i<candidates.size();i++){cout<<setw(4)<<candidates[i];}back(candidates,target,0);return res;}
};

力扣40.组合总和II

在这里插入图片描述
思路:

对于去重问题,我们得考虑,什么是树枝

根据题目,我们得看一点,集合中数据存在重复,然后每个数字只能用一次。这样我们就需要去加个判断了。
经典判断:
用来看这个数据我们处理过没有。

  // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过// used[i - 1] == false,说明同一树层candidates[i - 1]使用过
        bool visited[candidates.size()] ;for(int i = 0 ;i<candidates.size();i++){visited[i] = false;}

看完整代码:

class Solution {
public:vector<vector<int>> res;vector<int>path;int num = 0;void back(vector<int> candidates,int target,int index,bool visited[]){if(num > target){return;}if(num == target){// // 此处比较超出时间限制// vector<vector<int>>::iterator it = find(res.begin(), res.end(), path);// if(it == res.end()){//     res.push_back(path);// }res.push_back(path);return;}for(int i = index;i<candidates.size() && (num+candidates[i])<=target;i++){// 通过 visited进行去重// if(i>index && candidates[i] == candidates[i-1] && visited[i-1] ==){//     continue;// }// 判断同层,是否有遇到相同数字,如果遇到,则跳过,因为会重复if(i>index && candidates[i] == candidates[i-1] ){continue;}path.push_back(candidates[i]);visited[i] = true;num += candidates[i];back(candidates,target,i+1,visited);num -= candidates[i];visited[i] =false;path.pop_back();}}vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {bool visited[candidates.size()] ;for(int i = 0 ;i<candidates.size();i++){visited[i] = false;}sort(candidates.begin(),candidates.end()); // 排序for(int i = 0;i<candidates.size();i++){cout<<setw(4)<<candidates[i];}back(candidates,target,0,visited);return res;}
};

组合总结

其实组合问题,最经典就是77题的组合,其他题型无非就是在此基础上加入了路径的值,或者不能重复。

重点:

怎么去重?
去什么的重? 树枝上?还是同一树层?
怎么使用visited,来判断是否经历过这个节点?

  // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过// used[i - 1] == false,说明同一树层candidates[i - 1]使用过

组合问题总结

组合问题最基础还是77题,基本都是在此基础上的衍生。

核心回溯模板如下:

    void backtracking(int n, int k, int index){if(path.size() == k){res.push_back(path);return;}for(int i = index; i<=n;i++){path.push_back(i);backtracking(n,k,i+1);  // 不能重复选择path.pop_back();}}

当我们可以重复选择时,在递归时,就得注意一下了

backtracking(n,k,i);   // 可以重复选择

接下来就是关于去重问题了

去重分为两种:
树枝去重:used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
树层去重:used[i - 1] == false,说明同一树层candidates[i - 1]使用过

分割问题

我们先搞清楚,分割和组合得区别
比如字符串 abcdef
组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个…。
切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段…。

131.分割回文串

在这里插入图片描述
怎么将切割问题转化为抽象树呢?
在这里插入图片描述
注意:每切一个,我们得想想还剩下什么,剩下的又怎么切?什么时候去切?
核心代码:

       for(int i = index;i<s.size();i++){if(ishuiwen(s,index,i)){string str = s.substr(index, i - index + 1);path.push_back(str);back(s,i+1);path.pop_back();}}

完整代码:

class Solution {
public://` string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串`vector<vector<string>> res;vector<string> path;void back(string s,int index){if(index >= s.size()){res.push_back(path);return;}for(int i = index;i<s.size();i++){if(ishuiwen(s,index,i)){string str = s.substr(index, i - index + 1);path.push_back(str);back(s,i+1);path.pop_back();}}}bool ishuiwen(string s,int index ,int c){for(int i = index,j = c; i<j;i++,j--){if(s[i] == s[j]){continue;}else{return false;}}return true;}vector<vector<string>> partition(string s) {back(s,0);return res;}
};

力扣93.复原IP地址

在这里插入图片描述
思路:
我们此处怎么去切割?是不是就等于在字符串上加个 ’.‘ 是不是就属于切割?
在这里插入图片描述
思路:

这个也属于分割问题
我们按照其中的 ‘.’ 数来确实是否达标了
选择代码如下

if (count > 4) return; // 超过四部分,返回if (index == s.size() && count == 4) { // 完全匹配四部分res.push_back(path.substr(0, path.length() - 1)); // 移除最后的一个点return;}

那么如何在数层排列呢?
我们可以按照点数进行循环

        for (int i = 1; i <= 3; i++) {if (index + i > s.size()) break; // 超出字符串长度string str = s.substr(index, i);if (isValid(str)) {path += str + ".";back(s, index + i, count + 1);path.erase(path.length() - i - 1, i + 1); // 回溯,移除刚添加的部分及点}}

完整代码如下:

class Solution {
public:vector<string> res;string path;void back(string s, int index, int count) {if (count > 4) return; // 超过四部分,返回if (index == s.size() && count == 4) { // 完全匹配四部分res.push_back(path.substr(0, path.length() - 1)); // 移除最后的一个点return;}for (int i = 1; i <= 3; i++) {if (index + i > s.size()) break; // 超出字符串长度string str = s.substr(index, i);if (isValid(str)) {path += str + ".";back(s, index + i, count + 1);path.erase(path.length() - i - 1, i + 1); // 回溯,移除刚添加的部分及点}}}bool isValid(const string& str) {if (str.size() > 1 && str[0] == '0') return false; // 防止前导零int num = stoi(str);return num <= 255;}vector<string> restoreIpAddresses(string s) {back(s, 0, 0);return res;}
};

分割问题总结

其实分割问题,还是在 77 题组合问题上的修改。
我们在详细分化一下什么是组合,什么又是分割。

组合问题
组合问题主要是在不考虑元素顺序的情况下,从一组元素中选择若干元素的所有可能方式。

元素顺序不重要:选择的元素集合 [1,2] 和 [2,1] 被认为是相同的。
不切割原数据:直接在原数据集上进行选择。

分割问题
分割问题通常是指将一个字符串或数组分割成符合特定条件的多个子部分。

考虑元素顺序:分割的段必须维持原有元素的顺序。
切割原数据:需要在原数据中按顺序选择切点。

我们给出核心模板代码:

    void backtrack(const string& s, int start) {if (start == s.size()) { // 如果起始位置已经到达字符串末尾result.push_back(path);return;}for (int i = start; i < s.size(); i++) {if (isValid(s, start, i)) { // 判断当前分割是否有效path.push_back(s.substr(start, i - start + 1)); // 取子串并加入路径backtrack(s, i + 1); // 递归调用,从下一个字符开始新的分割path.pop_back(); // 回溯,撤销上一步的分割}}}bool isValid(const string& s, int start, int end) {// 判断 s[start...end] 是否符合条件,具体实现依据问题而定while (start < end) {if (s[start] != s[end])return false;start++;end--;}return true;}

子集问题

子集问题,就是组合问题的特殊版本。
子集问题,属于集合是无序的:那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!

以示例中nums = [1,2,3]为例把求子集抽象为树型结构,如下:
在这里插入图片描述

78.子集

在这里插入图片描述
思路:

我们在77题,基础上进行修改,就是无序的组合

完整代码:

class Solution {
public:vector<vector<int>> res;vector<int>path;void back(vector<int> nums,int index){res.push_back(path);if(index>=nums.size()){return;}for(int i = index;i<nums.size();i++){path.push_back(nums[i]);back(nums,i+1);path.pop_back();}}vector<vector<int>> subsets(vector<int>& nums) {// res.push_back(path);back(nums,0);return res;}
};

90.子集II

在这里插入图片描述
思路

我们先明白一点,这个子集不能是同重复的子集
是不是又回到当初的一个知识点?
判断是数层重复?还是树枝重复?
在这里插入图片描述

核心去重代码
我们用之前used方法进行判断:

            // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过// used[i - 1] == false,说明同一树层candidates[i - 1]使用过// 而我们要对同一树层使用过的元素进行跳过if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {continue;}
		 // 跳过这个递归层次,防止重复if(i > index && nums[i] == nums[i-1] && i >= 1){continue;}

完整代码:

class Solution {
public:vector<vector<int>> res;vector<int> path;void back(vector<int> &nums,int index){res.push_back(path);if(index >= nums.size()){return;}for(int i = index;i<nums.size();i++){// 跳过这个递归层次,防止重复if(i > index && nums[i] == nums[i-1] && i >= 1){continue;}path.push_back(nums[i]);back(nums,i+1);path.pop_back();}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(),nums.end());back(nums,0);return res;}
};

排列问题

排列,是属于有序的。和前文的组合不一样。比如: [1,2] 和 [2,1] 是两个集合。
可以看出元素1在[1,2]中已经使用过了,但是在[2,1]中还要在使用一次1,所以处理排列问题就不用使用startIndex了。
所以此时for循环如下,直接从0开始,不在需要startIndex索引了。

for (int i = 0; i < nums.size(); i++) 

那如何判断,是否数组里面有的已经使用过呢?此时我们就要继续用到上述的 used 函数。

  if (used[i] == true) continue; // path里已经收录的元素,直接跳过

在这里插入图片描述

46.全排列

在这里插入图片描述
思路:

我们还是可以按照,77题基础代码进行修改。
我们加入判断,看看是不是使用过的。

if (used[i] == true) continue; // path里已经收录的元素,直接跳过

完整代码如下:

class Solution {
public:vector<vector<int>> res;vector<int> path;void back(vector<int> &nums, vector<bool>& visited){if(path.size() == nums.size()){res.push_back(path);return;}for(int i = 0 ; i < nums.size();i++){if(visited[i]== true){continue;}path.push_back(nums[i]);visited[i] = true;back(nums,visited);visited[i] = false;path.pop_back();}}vector<vector<int>> permute(vector<int>& nums) {vector<bool> visited(nums.size(),false);back(nums,visited);return res;}
};

47.全排列 II

在这里插入图片描述
思路:

对比于上一个题,我们区别是什么?就是不重复的全排列。
不重复,第一时间想到的就是,树枝去重?还是数层去重呢?
此题,就是树层的去重。

核心代码如下:

if (used[i]) continue; // 如果当前元素已使用,则跳过
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue; // 跳过重复元素

完整代码如下:

#include <vector>
#include <algorithm>using namespace std;class Solution {
public:vector<vector<int>> permuteUnique(vector<int>& nums) {vector<vector<int>> results;vector<int> path;vector<bool> used(nums.size(), false); // 标记是否使用过该元素sort(nums.begin(), nums.end()); // 排序,以便跳过重复元素backtrack(nums, path, used, results);return results;}void backtrack(const vector<int>& nums, vector<int>& path, vector<bool>& used, vector<vector<int>>& results) {if (path.size() == nums.size()) {results.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; // 跳过重复元素path.push_back(nums[i]);used[i] = true;backtrack(nums, path, used, results);path.pop_back();used[i] = false;}}
};

排列问题总结

回溯法解决排列问题的原理:

选择:从一组数中选出一个元素作为排列的一部分,并标记该元素以避免重复使用。
约束:在选择下一个元素时,必须确保它尚未在当前排列中使用过(对于不包含重复元素的数组)。
目标:当排列达到数组的长度时,说明找到了一个可能的解决方案。

核心代码模板

#include <vector>using namespace std;class Solution {
public:vector<vector<int>> permute(vector<int>& nums) {vector<vector<int>> res;vector<int> path;vector<bool> used(nums.size(), false);backtrack(nums, path, used, res);return res;}void backtrack(vector<int>& nums, vector<int>& path, vector<bool>& used, 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; // 这个数已经用过了,跳过path.push_back(nums[i]);used[i] = true;backtrack(nums, path, used, res); // 继续递归填下一个数path.pop_back(); // 撤销选择used[i] = false; // 撤销选择}}
};

棋盘问题

51. N皇后

在这里插入图片描述
思路:

第一个想法是,如何抽象为一个树?
这种类型就是属于网格树

在这里插入图片描述
我们要写出判断是否符合要求的位置:

    bool isCanreach(int row,int col ,int n){// 同列for(int i = 0; i < row; i++){if(path[i][col] == 'Q'){return false;}}// 左上for(int i = row-1,j = col-1; i>=0&&j>=0; i--,j--){if(path[i][j] == 'Q'){return false;}}// 右上for(int i = row - 1, j = col+1; i>=0 && j<n; i--,j++){if(path[i][j] == 'Q'){return false;}}return true;}

再写出回溯部分代码:

    void back(int n,int row){if(row == n){res.push_back(path);return ;}for(int col = 0 ; col < n; col++){if(isCanreach(row,col,n)){path[row][col] = 'Q';back(n,row+1);path[row][col] = '.';}}}

完整代码如下:

class Solution {
public:vector<vector<string>> res;vector<string> path;void back(int n,int row){if(row == n){res.push_back(path);return ;}for(int col = 0 ; col < n; col++){if(isCanreach(row,col,n)){path[row][col] = 'Q';back(n,row+1);path[row][col] = '.';}}}bool isCanreach(int row,int col ,int n){// 同列for(int i = 0; i < row; i++){if(path[i][col] == 'Q'){return false;}}// 左上for(int i = row-1,j = col-1; i>=0&&j>=0; i--,j--){if(path[i][j] == 'Q'){return false;}}// 右上for(int i = row - 1, j = col+1; i>=0 && j<n; i--,j++){if(path[i][j] == 'Q'){return false;}}return true;}vector<vector<string>> solveNQueens(int n) {path.resize(n, string(n, '.'));back(n,0);return res;}
};

37. 解数独在这里插入图片描述

思路:
我们还是得先想想这种问题,如何抽象为一个抽象树呢?
在这里插入图片描述
判断棋盘是否合法有如下三个维度:

同行是否重复
同列是否重复
9宫格里是否重复

    bool isCanreach(vector<vector<char>>& board,int row,int col,char c){for(int i = 0; i<9;i++){if(board[row][i] == c){return false;}if(board[i][col] == c){return false;}// 3*3方格if(board[3*(row/3)+i/3][3*(col/3)+i%3] == c){return false;}}return true;}

一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!

    bool back(vector<vector<char>>& board){for(int i = 0;i<9;i++){for(int j = 0;j<9;j++){if(board[i][j] == '.'){for(char c = '1' ; c<='9';c++){if(isCanreach(board,i,j,c)){board[i][j] = c;if(back(board)){return true;}// 回溯board[i][j] = '.';}}// 代表所有数字都不行return false;}}}return true;}

完整代码:

class Solution {
public://vector<vector<char>> res;bool back(vector<vector<char>>& board){for(int i = 0;i<9;i++){for(int j = 0;j<9;j++){if(board[i][j] == '.'){for(char c = '1' ; c<='9';c++){if(isCanreach(board,i,j,c)){board[i][j] = c;if(back(board)){return true;}// 回溯board[i][j] = '.';}}// 代表所有数字都不行return false;}}}return true;}bool isCanreach(vector<vector<char>>& board,int row,int col,char c){for(int i = 0; i<9;i++){if(board[row][i] == c){return false;}if(board[i][col] == c){return false;}// 3*3方格if(board[3*(row/3)+i/3][3*(col/3)+i%3] == c){return false;}}return true;}void solveSudoku(vector<vector<char>>& board) {back(board);}
};

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

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

相关文章

未来已来:Spring Cloud引领微服务新纪元

文章目录 1. 引言1.1 微服务架构的兴起与挑战1.2 引入Spring Cloud作为微服务解决方案的重要性 2. 背景介绍2.1 微服务架构概述2.2 Spring Cloud的定位2.3 Spring Cloud特性概览 3. Spring Cloud核心组件3.1 Eureka - 服务发现3.2 Hystrix - 断路器3.3 Ribbon - 客户端负载均衡…

Python vs MATLAB:选择深度学习的首选编程语言

Python vs MATLAB&#xff1a;选择深度学习的首选编程语言 在深度学习领域&#xff0c;编程语言的选择对于初学者的学习路径和未来的职业发展至关重要。目前&#xff0c;Python和MATLAB都是进行科学计算和数据分析的流行工具&#xff0c;但它们在深度学习社区中的应用和受欢迎…

构建教育新未来:智慧校园平台的深度解读与全景呈现

引言 在全球数字化转型的大潮中&#xff0c;智慧校园平台作为教育信息化的重要载体&#xff0c;正以前所未有的姿态颠覆传统的教育模式&#xff0c;引领教育行业步入一个崭新的时代。这个融合了大数据、人工智能、云计算、物联网等一系列前沿科技的平台&#xff0c;以其强大的功…

C++笔记:类和对象(一)

一.类的引入 1.类(class)是C中的一种特殊的数据结构&#xff0c;它封装了数据和操作这些数据的函数。 2.类中的数据被称为成员变量&#xff0c;而函数则被称为成员函数。 3.类可以被看作是一种模板&#xff0c;用于创建具有相同属性和行为的多个对象。 在C语言中结构体是一种复…

数字化运维体系:重塑IT运维的未来面貌

随着信息技术的飞速发展&#xff0c;传统运维模式已难以满足现代企业日益增长的复杂需求。在此背景下数字化运维体系应运而生&#xff0c;以其独特的视角和方法论为IT运维领域带来了革新性的变革。本文将深入探讨数字化运维体系的定义、核心要素&#xff0c;并阐述如何通过这一…

rust开发web服务器框架,github排名对比

Rocket Star最多的框架 github仓库地址&#xff1a;GitHub - rwf2/Rocket: A web framework for Rust. Rocket 是一个针对 Rust 的异步 Web 框架&#xff0c;重点关注可用性、安全性、可扩展性和速度。 Axum 异步运行时 githuh仓库地址&#xff1a;GitHub - tokio-rs/axum: …

Redis—图文详解高可用原因

本文不会讲解Redis的用途&#xff0c;关于用途会发另一片文章讲解&#xff0c;本文主要讲的是高可用的原理。 Redis高可用主要有以下三个原因&#xff1a;主从模式(上一篇讲Kafka的文章里有涉及到)&#xff0c;哨兵模式&#xff0c;Redis-Cluster(Redis集群)。 什么是主从模式…

【2024高校网络安全管理运维赛】巨细记录!

2024高校网络安全管理运维赛 文章目录 2024高校网络安全管理运维赛MISC签到考点&#xff1a;动态图片分帧提取 easyshell考点&#xff1a;流量分析 冰蝎3.0 Webphpsql考点&#xff1a;sql万能钥匙 fileit考点&#xff1a;xml注入 外带 Cryptosecretbit考点&#xff1a;代码阅读…

数据结构与算法-时间复杂度

这篇文章会讲到时间频度&#xff0c;时间复杂度&#xff0c;一些排序算法的平均时间复杂度与最坏时间复杂度 目录 1.度量一个程序&#xff08;算法&#xff09;的两种方法 1&#xff09;事后统计法 2&#xff09;事前估算法 2.时间频度 1.基本介绍 算法1&#xff1a; 算…

C语言 | Leetcode C语言题解之第86题分隔链表

题目&#xff1a; 题解&#xff1a; struct ListNode* partition(struct ListNode* head, int x) {struct ListNode* small malloc(sizeof(struct ListNode));struct ListNode* smallHead small;struct ListNode* large malloc(sizeof(struct ListNode));struct ListNode* …

SpringCloud微服务之Eureka、Ribbon、Nacos详解

SpringCloud微服务之Eureka、Ribbon、Nacos详解 1、认识微服务1.1、单体架构1.2、分布式架构1.3、微服务1.4、SpringCloud 2、服务拆分与远程调用2.1、服务拆分的原则2.2、服务拆分示例2.2、提供者与消费者 3、Eureka注册中心3.1、Eureka的结构和作用3.2、搭建eureka-server3.2…

使用IIS部署Vue项目

前提 使用IIS部署Vue项目&#xff0c;后端必须跨域&#xff0c;不要在Vue中用proxy跨域&#xff0c;那个只在dev环境中有用&#xff01; IIS安装&#xff0c;不用全部打勾&#xff0c;有些他默认就是方块 ■ 选择性安装的&#xff0c;就维持原样就可以。 添加网站配置 右键…

4.1 编写程序,从键盘接收一个小写字母,然后找出他的前导字符和后续字符,再按顺序显示这三个字符

方法一&#xff1a; 运行效果&#xff1a; 输入B&#xff0c;输出显示ABC&#xff1b;输入A&#xff0c;输出显示AB 思路&#xff1a; 1、通过键盘输入接收一个字母。 2、将输入的字母减去1&#xff0c;得到前导字符&#xff0c;然后输出。 3、将输入的字母加上1&#xff0c;得…

公有云Linux模拟UDP端口并抓包

目录 写在前面操作步骤服务端开启UDP端口并监听客户端连接Wireshark抓包查看 写在前面 关于具体的操作&#xff0c;请参考我的上一篇文章 公有云Linux模拟TCP三次挥手与四次握手&#xff08;Wireshark抓包验证版&#xff09; 在本文&#xff0c;仅介绍与上一篇不同的地方。 操…

机器学习特征降维

目录 特征降维概念 低方差过滤法 PCA主成分分析 相关系数法 小结 特征降维概念 特征对训练模型时非常重要的&#xff1b;用于训练的数据集包含一些不重要的特征&#xff0c;可能导致模型性能不好、泛化性能不佳&#xff1b;例如&#xff1a; 某些特征的取值较为接近&…

MySQL查询篇-聚合函数-窗口函数

文章目录 distinct 关键字聚合函数常见的聚合函数group by和having 分组过滤 窗口函数with as窗口聚合函数排名窗口函数值窗口函数 distinct 关键字 distinct 去重数据&#xff0c;ps:null值也会查出来 select distinct column from table;聚合函数 常见的聚合函数 select …

YOLOv8独家原创改进: AKConv(可改变核卷积)

1.AKConv原理介绍 地址:2311.11587 (arxiv.org) 摘要:基于卷积运算的神经网络在深度学习领域取得了令人瞩目的成果,但标准卷积运算存在两个固有的缺陷。一方面,卷积运算仅限于局部窗口,无法捕获其他位置的信息, 并且它的采样形状是固定的。 另一方面,卷积核的大小固定为…

08.4.grafana自定义图形并直接数据库取值

grafana自定义图形并直接数据库取值 自定义添加油表图形 选择gauge图形&#xff0c;并且配置对应设定值&#xff0c;点击应用 如图所示&#xff0c;可以看到仪表盘上的值是zabbix上取得值 配置grafana直接数据库取值 添加mysql数据源 添加后进行配置&#xff0c;我这…

通过内网穿透实现远程访问个人电脑资源详细过程(免费)(NatApp + Tomcat)

目录 1. 什么是内网穿透 2. 内网穿透软件 3. NatApp配置 4. 启动NatApp 5. 通过内网穿透免费部署我们的springboot项目 通过内网穿透可以实现远程通过网络访问电脑的资源&#xff0c;本文主要讲述通过内网穿透实现远程访问个人电脑静态资源的访问&#xff0c;下一章节将讲…

Mobilenet四代网络模型架构

一、Mobilenet v1 MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications 论文地址:https://arxiv.org/abs/1704.04861https://arxiv.org/abs/1704.04861 1.概述 Mobilenet是一个用于移动端和嵌入式的神经网络,其核心思想是采用深度可分离…