当前位置: 首页 > article >正文

算法之回溯法

回溯法

  • 回溯法
    • 定义与概念
    • 核心思想
    • 回溯法的一般框架
      • 伪代码表示
      • C语言实现框架
    • 回溯法的优化技巧
      • 剪枝策略
      • 实现剪枝的C语言示例
      • 记忆化搜索
    • 案例分析
      • N皇后问题
      • 子集和问题
      • 全排列问题
      • 寻路问题
    • 回溯法的可视化理解
      • 决策树
      • 状态空间树
      • 回溯过程
    • 回溯法与其他算法的比较
      • 回溯法与动态规划的区别
      • 回溯法与贪心算法的区别
    • 总结
      • 应用场景总结
      • 优化技巧总结

回溯法

定义与概念

回溯法是一种通过探索所有可能的候选解来找出所有解的算法。它采用试错的思想,尝试分步解决一个问题,在分步解决问题的过程中,当发现现有的分步答案不能得到有效的正确的解答时,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。

回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况:

  • 找到一个可能存在的正确答案
  • 在尝试了所有可能的分步方法后宣告该问题无解

核心思想

典型的回溯算法通常包括以下步骤:

选择:在解空间中,进行一次选择,生成一个可能的解。

约束条件:检查当前的选择是否满足问题的限制条件。

判断:判断当前的选择是否是问题的解决方案。

回溯:如果当前选择不符合约束条件或者不是最终解,就撤销这次选择,回到之前的状态,并尝试其他的选择。

重复:重复上述步骤,直到找到问题的解决方案或者穷尽所有可能性。

典型的应用场景包括:

  • 组合求和问题:寻找集合中符合特定条件的子集合或组合。
  • 排列问题:如全排列、字符串排列等。
  • 棋盘游戏:例如数独、八皇后等问题。
  • 图搜索:在图中寻找路径、回路等问题。

回溯算法在解决组合优化问题时通常具有高效的灵活性,但随着问题规模的增加,其时间复杂度可能会指数级增长。因此,在实际应用中,通常会对算法进行优化,比如剪枝、启发式搜索等方法,以提高效率。

回溯法的一般框架

伪代码表示

回溯法的一般框架可以用以下伪代码表示:

void backtrack(Candidate* candidate) {// 检查是否找到解决方案if (find_solution(candidate)) {output_solution(candidate);return;}// 获取候选列表Candidate next_candidates[MAX_CANDIDATES];int candidate_count = 0;generate_candidates(candidate, next_candidates, &candidate_count);// 尝试每个候选解for (int i = 0; i < candidate_count; i++) {if (is_valid(&next_candidates[i])) {// 放置候选解place_candidate(candidate, &next_candidates[i]);// 递归搜索backtrack(candidate);// 移除候选解(回溯)remove_candidate(candidate, &next_candidates[i]);}}
}

其中:

  • find_solution():检查当前候选解是否是一个完整的解
  • output_solution():输出找到的解决方案
  • generate_candidates():生成当前可以选择的候选解列表
  • is_valid():检查当前候选解是否满足约束条件
  • place_candidate():将当前候选解放入解集合中
  • remove_candidate():将当前候选解从解集合中移除(回溯)
  • MAX_CANDIDATES:候选解数组的最大容量
  • Candidate:表示候选解的数据结构

C语言实现框架

以下是回溯法的C语言通用框架实现:

#include <stdio.h>
#include <stdbool.h>// 问题的状态结构
typedef struct {// 问题特定的状态变量int n;              // 问题规模int* solution;      // 当前解int depth;          // 当前搜索深度// 其他需要的状态变量
} State;// 初始化状态
void initState(State* state, int n) {state->n = n;state->depth = 0;state->solution = (int*)malloc(n * sizeof(int));// 初始化其他状态变量
}// 检查是否找到解
bool isSolution(State* state) {// 实现检查当前状态是否是一个完整的解的逻辑return state->depth == state->n; // 示例:当深度等于问题规模时找到解
}// 处理找到的解
void processSolution(State* state) {printf("找到一个解: ");for (int i = 0; i < state->n; i++) {printf("%d ", state->solution[i]);}printf("\n");
}// 生成候选
void generateCandidates(State* state, int candidates[], int* count) {// 实现生成候选的逻辑*count = 0;// 填充candidates数组并更新count
}// 检查候选是否有效
bool isValid(State* state, int candidate) {// 实现检查候选是否有效的逻辑return true; // 示例:所有候选都有效
}// 做出选择
void makeMove(State* state, int candidate) {// 实现做出选择的逻辑state->solution[state->depth] = candidate;state->depth++;
}// 撤销选择(回溯)
void unmakeMove(State* state) {// 实现撤销选择的逻辑state->depth--;
}// 回溯算法主体
void backtrack(State* state) {if (isSolution(state)) {processSolution(state);return;}int candidates[100]; // 假设最多100个候选int candidateCount;generateCandidates(state, candidates, &candidateCount);for (int i = 0; i < candidateCount; i++) {if (isValid(state, candidates[i])) {makeMove(state, candidates[i]);backtrack(state);unmakeMove(state);}}
}// 主函数
int main() {int n = 4; // 问题规模State state;initState(&state, n);backtrack(&state);free(state.solution);return 0;
}

回溯法的优化技巧

剪枝策略

剪枝是回溯法中最重要的优化技巧,它可以显著减少搜索空间,提高算法效率。常见的剪枝策略包括:

  1. 可行性剪枝:在搜索过程中,如果当前状态已经不可能产生有效解,则立即回溯。

  2. 最优性剪枝:在求解最优化问题时,如果当前状态的解不可能优于已知的最优解,则立即回溯。

  3. 对称性剪枝:利用问题的对称性,避免搜索等价的状态。

  4. 启发式剪枝:使用启发式函数估计当前状态的潜力,优先搜索更有希望的状态。

实现剪枝的C语言示例

以下是在子集和问题中实现剪枝的示例:

// 子集和问题的结构定义
typedef struct {int* set;           // 原始集合int set_size;       // 集合大小int target_sum;     // 目标和int current_sum;    // 当前和int* current;       // 当前选择状态
} SubsetSum;// 打印子集
void printSubset(SubsetSum* problem) {printf("{ ");for (int i = 0; i < problem->set_size; i++) {if (problem->current[i]) {printf("%d ", problem->set[i]);}}printf("}\n");
}// 带剪枝的子集和问题回溯函数
void subsetSumBacktrackWithPruning(SubsetSum* problem, int index, int* solutions_count) {// 剪枝1:如果当前和已经等于目标和,直接输出解if (problem->current_sum == problem->target_sum) {(*solutions_count)++;printf("解决方案 %d: ", *solutions_count);printSubset(problem);return;}// 剪枝2:如果当前和已经超过目标和,直接回溯if (problem->current_sum > problem->target_sum) {return;}// 剪枝3:如果即使将剩余所有元素都选上也无法达到目标和,直接回溯int remaining_sum = 0;for (int i = index; i < problem->set_size; i++) {remaining_sum += problem->set[i];}if (problem->current_sum + remaining_sum < problem->target_sum) {return;}// 基本情况:已经处理完所有元素if (index == problem->set_size) {return;}// 选择当前元素problem->current[index] = 1;problem->current_sum += problem->set[index];subsetSumBacktrackWithPruning(problem, index + 1, solutions_count);// 回溯,不选当前元素problem->current_sum -= problem->set[index];problem->current[index] = 0;subsetSumBacktrackWithPruning(problem, index + 1, solutions_count);
}

记忆化搜索

记忆化搜索是一种结合了动态规划思想的回溯优化技术,它通过存储已经计算过的状态结果,避免重复计算。

// 记忆化搜索示例(斐波那契数列)
int memo[100] = {0}; // 记忆数组,初始化为0int fibonacci(int n) {// 基本情况if (n <= 1) return n;// 如果已经计算过,直接返回结果if (memo[n] != 0) return memo[n];// 计算结果并存储memo[n] = fibonacci(n-1) + fibonacci(n-2);return memo[n];
}

案例分析

N皇后问题

N皇后问题是一个经典的问题:在N×N格的棋盘上放置N个皇后,使得它们不能互相攻击。按照国际象棋的规则,皇后可以攻击同一行、同一列或同一斜线上的棋子。

以下是N皇后问题的C语言实现:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>#define N 8  // 棋盘大小和皇后数量// 打印棋盘
void printSolution(int board[N][N]) {for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {printf("%c ", board[i][j] ? 'Q' : '.');}printf("\n");}printf("\n");
}// 检查在board[row][col]位置放置皇后是否安全
bool isSafe(int board[N][N], int row, int col) {int i, j;// 检查这一行的左侧for (i = 0; i < col; i++) {if (board[row][i]) {return false;}}// 检查左上对角线for (i = row, j = col; i >= 0 && j >= 0; i--, j--) {if (board[i][j]) {return false;}}// 检查左下对角线for (i = row, j = col; j >= 0 && i < N; i++, j--) {if (board[i][j]) {return false;}}return true;
}// 使用回溯法解决N皇后问题
bool solveNQUtil(int board[N][N], int col, int* solutionCount) {// 基本情况:如果所有皇后都被放置if (col >= N) {(*solutionCount)++;printf("解决方案 %d:\n", *solutionCount);printSolution(board);return true; // 找到一个解决方案}bool res = false;// 考虑这一列并尝试将皇后放在这一列的所有行中for (int i = 0; i < N; i++) {// 检查皇后是否可以放在board[i][col]if (isSafe(board, i, col)) {// 放置皇后在board[i][col]board[i][col] = 1;// 递归放置其余的皇后// 修改这里以找到所有解决方案,而不是只找到一个就返回solveNQUtil(board, col + 1, solutionCount);res = true; // 标记找到了至少一个解决方案// 回溯,移除皇后,继续尝试其他位置board[i][col] = 0; // 回溯}}// 如果皇后不能放在这一列的任何行,则返回falsereturn res;
}// 解决N皇后问题的包装函数
void solveNQ() {int board[N][N] = {0}; // 初始化棋盘int solutionCount = 0;if (!solveNQUtil(board, 0, &solutionCount)) {printf("没有解决方案\n");} else {printf("总共找到 %d 个解决方案\n", solutionCount);}
}int main() {solveNQ();return 0;
}

子集和问题

子集和问题是指:给定一个整数集合和一个目标和,找出集合中所有和为目标值的子集。

/*** 回溯法解决子集和问题* @param problem 子集和问题结构* @param index 当前处理的元素索引* @param solutions_count 找到的解决方案计数*/
void subsetSumBacktrack(SubsetSum* problem, int index, int* solutions_count) {// 基本情况:已经处理完所有元素if (index == problem->set_size) {// 检查是否找到一个解if (problem->current_sum == problem->target_sum) {(*solutions_count)++;printf("解决方案 %d: ", *solutions_count);printSubset(problem);}return;}// 不选当前元素problem->current[index] = 0;subsetSumBacktrack(problem, index + 1, solutions_count);// 选择当前元素(只有当不超过目标和时才选择)if (problem->current_sum + problem->set[index] <= problem->target_sum) {problem->current[index] = 1;problem->current_sum += problem->set[index];subsetSumBacktrack(problem, index + 1, solutions_count);// 回溯problem->current_sum -= problem->set[index];problem->current[index] = 0;}
}

全排列问题

全排列问题是指:给定一个不含重复数字的序列,返回其所有可能的全排列。

// 全排列问题的结构定义
typedef struct {int* nums;          // 原始数字序列int size;           // 序列大小int* result;        // 当前排列结果bool* used;         // 标记数字是否已使用int depth;          // 当前深度
} Permutation;// 打印排列
void printPermutation(Permutation* problem) {printf("{ ");for (int i = 0; i < problem->size; i++) {printf("%d ", problem->result[i]);}printf("}\n");
}/*** 回溯法解决全排列问题* @param problem 全排列问题结构* @param solutions_count 找到的解决方案计数*/
void permutationBacktrack(Permutation* problem, int* solutions_count) {// 基本情况:已经生成完整的排列if (problem->depth == problem->size) {(*solutions_count)++;printf("排列 %d: ", *solutions_count);printPermutation(problem);return;}// 尝试在当前位置放置每个未使用的数字for (int i = 0; i < problem->size; i++) {// 如果数字未被使用if (!problem->used[i]) {// 选择当前数字problem->result[problem->depth] = problem->nums[i];problem->used[i] = true;problem->depth++;// 递归生成下一个位置的数字permutationBacktrack(problem, solutions_count);// 回溯problem->depth--;problem->used[i] = false;}}
}

寻路问题

寻路问题是指在一个迷宫中找出从起点到终点的路径。以下是一个简单的迷宫寻路问题的C语言实现:

#include <stdio.h>
#include <stdbool.h>#define N 5 // 迷宫大小// 迷宫:0表示可以通过的路径,1表示墙
int maze[N][N] = {{0, 1, 0, 0, 0},{0, 1, 0, 1, 0},{0, 0, 0, 0, 0},{0, 1, 1, 1, 0},{0, 0, 0, 1, 0}
};// 解决方案:记录路径,1表示路径的一部分
int solution[N][N] = {0};// 检查(x,y)是否是迷宫中的有效位置
bool isValidPosition(int x, int y) {return (x >= 0 && x < N && y >= 0 && y < N && maze[x][y] == 0);
}// 使用回溯法解决迷宫问题
bool solveMazeUtil(int x, int y) {// 如果(x,y)是目标位置,返回trueif (x == N-1 && y == N-1) {solution[x][y] = 1;return true;}// 检查(x,y)是否是有效位置if (isValidPosition(x, y)) {// 标记(x,y)为路径的一部分solution[x][y] = 1;// 向右移动if (solveMazeUtil(x+1, y)) {return true;}// 向下移动if (solveMazeUtil(x, y+1)) {return true;}// 向左移动if (solveMazeUtil(x-1, y)) {return true;}// 向上移动if (solveMazeUtil(x, y-1)) {return true;}// 如果没有方向可以到达目标,回溯solution[x][y] = 0;return false;}return false;
}// 解决迷宫问题的包装函数
bool solveMaze() {if (!solveMazeUtil(0, 0)) {printf("没有解决方案\n");return false;}// 打印解决方案printf("解决方案:\n");for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {printf("%d ", solution[i][j]);}printf("\n");}return true;
}int main() {solveMaze();return 0;
}

回溯法的可视化理解

回溯法本质上是一种深度优先搜索(DFS)的过程,通过可视化工具可以更直观地理解其工作原理。

决策树

回溯法可以通过决策树来可视化理解。每个节点代表一个状态,每条边代表一个选择。回溯法就是在这棵树上进行深度优先搜索,寻找满足条件的路径。

                    [Root]/  |  \/   |   \[A]   [B]   [C]  <- 第一层选择/ \    / \    / \/   \  /   \  /   \[D]  [E][F]  [G][H]  [I]  <- 第二层选择

在这个决策树中:

  • 从根节点开始,我们有三个可能的选择:A、B或C
  • 选择A后,我们可以进一步选择D或E
  • 选择B后,我们可以进一步选择F或G
  • 选择C后,我们可以进一步选择H或I

回溯法会先尝试一条路径(如Root→A→D),如果发现这条路径不满足条件,就回溯到上一个节点(A),然后尝试另一条路径(Root→A→E),依此类推。

状态空间树

状态空间树是回溯法中另一种重要的可视化工具,它展示了问题的所有可能状态及其转换关系。

对于N皇后问题,状态空间树的每一层代表在棋盘的一列中放置皇后,每个节点的子节点代表在下一列的不同行中放置皇后的选择。

                    [空棋盘]/   |   \/    |    \[第1行]  [第2行]  [第3行] ... [第N行]  <- 第1列的选择/  |  \    /  |  \    /  |  \/   |   \  /   |   \  /   |   \[第1行] [第2行] [第3行]...  <- 第2列的选择(根据约束条件筛选)

在这个状态空间树中:

  • 第一层表示在第1列的N个可能位置放置皇后
  • 第二层表示在第2列的可能位置放置皇后,但这些位置必须满足不与第1列的皇后相互攻击
  • 依此类推,每一层的选择都受到之前所有选择的约束

回溯过程

以3皇后问题为例,回溯过程可以表示为:

  1. 在第1列放置皇后(尝试第1行)
  2. 在第2列放置皇后(由于第1行已被攻击,尝试第2行)
  3. 在第3列放置皇后(由于第1行和第2行已被攻击,尝试第3行)
  4. 发现无法放置所有皇后,回溯到第2步
  5. 在第2列移除皇后,尝试第3行
  6. 在第3列放置皇后(由于第1行和第3行已被攻击,尝试第2行)
  7. 找到一个解决方案

这个过程可以用以下棋盘序列来可视化:

步骤1: 在第1列第1行放置皇后
Q . .
. . .
. . .步骤2: 在第2列第2行放置皇后
Q . .
. Q .
. . .步骤3: 尝试在第3列放置皇后,但没有有效位置
(回溯到步骤2)步骤4: 移除第2列的皇后
Q . .
. . .
. . .步骤5: 在第2列第3行放置皇后
Q . .
. . .
. Q .步骤6: 在第3列第2行放置皇后
Q . .
. . Q
. Q .找到解决方案!

通过这种可视化方式,我们可以清晰地看到回溯法如何系统地探索解空间,并在遇到死胡同时如何回溯并尝试其他路径。

回溯法与其他算法的比较

算法特点适用场景典型问题时间复杂度空间复杂度
回溯法尝试所有可能的解,遇到不满足条件的解则回溯需要找到所有可能的解八皇后问题、数独、全排列指数级 O(b^d)O(d)
贪心算法每一步选择当前最优解问题具有贪心选择性质最小生成树、哈夫曼编码多项式级O(n)
动态规划将问题分解为子问题,存储子问题的解问题具有重叠子问题和最优子结构背包问题、最长公共子序列多项式级O(n^2)
分治法将问题分解为独立的子问题,合并子问题的解问题可以分解为独立的子问题归并排序、快速排序O(n log n)O(log n)
分支限界法类似回溯但使用队列而非栈,可以找到最优解求解最优化问题旅行商问题、作业调度指数级指数级

回溯法与动态规划的区别

  1. 问题类型

    • 回溯法:适用于找出所有可能解或所有满足条件的解。
    • 动态规划:适用于找出最优解。
  2. 重叠子问题

    • 回溯法:通常不处理重叠子问题,可能会重复计算。
    • 动态规划:通过记忆化存储子问题的解,避免重复计算。
  3. 搜索方式

    • 回溯法:深度优先搜索。
    • 动态规划:通常是自底向上或自顶向下的方式构建解。

回溯法与贪心算法的区别

  1. 决策方式

    • 回溯法:考虑所有可能的选择,并在需要时回溯。
    • 贪心算法:每一步都选择当前看起来最好的选择,不会回溯。
  2. 最优性

    • 回溯法:可以找到全局最优解。
    • 贪心算法:只能保证局部最优,不一定能找到全局最优解。
  3. 效率

    • 回溯法:时间复杂度通常较高,可能是指数级的。
    • 贪心算法:时间复杂度通常较低,多为多项式级别。

总结

回溯法是一种强大的算法设计技术,适用于需要探索所有可能解的问题。它通过系统地尝试所有可能的解,并在发现当前路径不可行时回溯到上一步,继续探索其他可能的路径。虽然回溯法的时间复杂度可能很高,但通过合理的剪枝策略,可以显著提高算法的效率。

回溯法的核心思想是"试探+回溯",它是解决组合优化问题、约束满足问题等的有效方法。在实际应用中,回溯法常常与其他算法技术(如动态规划、贪心算法等)结合使用,以解决更复杂的问题。

应用场景总结

  1. 组合问题:如子集和问题、组合总和问题等。
  2. 排列问题:如全排列、字符串排列等。
  3. 棋盘问题:如N皇后问题、数独问题等。
  4. 图搜索问题:如迷宫寻路、图的着色问题等。
  5. 约束满足问题:如数独、填字游戏等。

优化技巧总结

  1. 剪枝:通过各种策略减少搜索空间。
  2. 启发式搜索:优先搜索更有希望的状态。
  3. 记忆化:存储已计算过的状态结果,避免重复计算。
  4. 位运算优化:使用位运算加速状态表示和操作。
  5. 并行化:在多核环境下并行搜索不同的状态空间。

相关文章:

算法之回溯法

回溯法 回溯法定义与概念核心思想回溯法的一般框架伪代码表示C语言实现框架 回溯法的优化技巧剪枝策略实现剪枝的C语言示例记忆化搜索 案例分析N皇后问题子集和问题全排列问题寻路问题 回溯法的可视化理解决策树状态空间树回溯过程 回溯法与其他算法的比较回溯法与动态规划的区…...

武汉昊衡科技OLI光纤微裂纹检测仪:高密度光器件的精准守护者

随着AI技术应用越来越广&#xff0c;算力需求激增&#xff0c;光通信系统正加速向小型化、高密度、多通道方向演进。硅光芯片、高速光模块等核心器件内部的光纤通道数量成倍增加&#xff0c;波导结构愈发精细&#xff0c;传统检测手段因分辨率不足、效率低下&#xff0c;难以精…...

SQL 函数进行左边自动补位fnPadLeft和FORMAT

目录 1.问题 2.解决 方式1 方式2 3.结果 1.问题 例如在SQL存储过程中&#xff0c;将1 或10 或 100 长度不足的时候&#xff0c;自动补足长度。 例如 1 → 001 10→ 010 100→100 2.解决 方式1 SELECT FORMAT (1, 000) AS FormattedNum; SELECT FORMAT(12, 000) AS Form…...

Tailwind CSS实战:快速构建定制化UI的新思路

引言 在当今快节奏的前端开发环境中&#xff0c;开发者不断寻找能够提高效率并保持灵活性的工具。Tailwind CSS作为一个功能型优先的CSS框架&#xff0c;正在改变开发者构建用户界面的方式。与Bootstrap和Material UI等传统组件库不同&#xff0c;Tailwind不提供预设组件&…...

【数据可视化-25】时尚零售销售数据集的机器学习可视化分析

🧑 博主简介:曾任某智慧城市类企业算法总监,目前在美国市场的物流公司从事高级算法工程师一职,深耕人工智能领域,精通python数据挖掘、可视化、机器学习等,发表过AI相关的专利并多次在AI类比赛中获奖。CSDN人工智能领域的优质创作者,提供AI相关的技术咨询、项目开发和个…...

UML 活动图深度解析:以在线购物系统为例

目录 一、UML 活动图的基本构成要素 二、题目原型 三、在线购物系统用户购物活动图详细剖析 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;节点分析 三、注意事项 四、活动图绘画 五、UML 活动图在软件开发中的关键价值 六、总结 在软件开发与系统设计领…...

利用车联网中的 V2V 通信技术传播公平的紧急信息

与移动自组织网络 (MANET) 相比,车载自组织网络 (VANET) 的节点移动速度更快。网络连接的节点可以在自身内部或其他基础设施之间交换安全或非安全消息,例如车对车 (V2V) 或车对万物 (V2X)。在车载通信中,紧急消息对于安全至关重要,必须分发给所有节点,以提醒它们注意潜在问…...

文件的读取操作

#import time # 导入time 库 # 打开文件 fileopen("E:\Dasktape/python_test.txt","r",encoding"UTF-8")# 读取文件 print(f"读取文件的所有内容内容:{file.read()}\n") #\n是换行字符 print(f"读取10个字节的文件内容:{file.re…...

数学基础 -- 欧拉恒等式的魅力:让复数旋转起来!

公式推导&#xff1a; e i π − 1 e^{i\pi} -1 eiπ−1 被誉为数学中最美的公式之一&#xff0c;它连接了五个数学中最重要的常数&#xff1a; e i π 1 0 (欧拉恒等式) e^{i\pi} 1 0 \tag{欧拉恒等式} eiπ10(欧拉恒等式) 这不仅是巧合&#xff0c;而是复数与三角函数…...

【android bluetooth 协议分析 06】【l2cap详解 6】【L2CA_Register函数解析】

L2CA_Register() 函数的实现&#xff0c;它的作用是&#xff1a; 注册一个 L2CAP 服务&#xff08;基于 PSM&#xff09;并设置回调函数、MTU、安全等级、传输模式等信息&#xff0c;供 L2CAP 层用于处理连接、配置、数据、断开等事件。 1. L2CA_Register2/L2CA_Register 参数…...

【MFC】 VS2022打开低版本的MFC,双击.rc文件,DIalog加载失败,页面弹窗fatal error RC***:cannot open*****

打开以前的MFC示例报错&#xff0c;打开VS2019的实例以及更早VS版本的实例都一样,打不开&#xff0c;还报错&#xff1b; 错误 MSB8041 此项目需要 MFC 库。从 Visual Studio 安装程序(单个组件选项卡)为正在使用的任何工具集和体系结构安装它们。 GxCameraEvents_VS2015 C:\P…...

Centos9 安装 nginx 及配置

1. 安装nginx 安装依赖软件&#xff0c;安装之前可以看一下是否已经安装过以下软件&#xff0c;dnf list installed | grep zlib dnf install gcc-c dnf install zlib dnf install pcre pcre-devel dnf install openssl openssl-devel下载nginx&#xff0c;这里是下载到opt文…...

使用Handsontable实现动态表格和下载表格

1.效果 2.实现代码 首先要加载Handsontable&#xff0c;在示例中我是cdn的方式引入的&#xff0c;vue的话需要下载插件 let hot null;var exportPlugin null;function showHandsontable(param) {const container document.getElementById("hot-container");// 如果…...

Action:Update your application‘s configuration

在使用Maven项目时&#xff0c;有一个报错信息是&#xff1a;Update your applications configuration 这类问题&#xff0c;就是我们的application.yml文件 或者 application.properties文件 内容哪里写错了 最有可能就是对齐方式有问题...

【计算机网络】IP地址

IPv4 五类地址 1.0.0.0 ~ 126.255.255.255A类子网8位&#xff0c;主机24位128.0.0.0 ~ 191.255.255.255B类子网16位&#xff0c;主机16位192.0.0.0 ~ 223.255.255.255C类子网24位&#xff0c;主机8位224.0.0.0 ~ 239.255.255.255D类不分网络地址和主机地址&#xff0c;作为组播…...

Rundeck 介绍及安装:自动化调度与执行工具

Rundeck介绍 概述&#xff1a;Rundeck 是什么&#xff1f; Rundeck 是一款开源的自动化调度和任务执行工具&#xff0c;专为运维场景设计&#xff0c;帮助工程师通过统一的平台管理和执行跨系统、跨节点的任务。它由 PagerDuty 维护&#xff08;2016 年收购&#xff09;&#…...

vue element使用el-table时,切换tab,table表格列项发生错位问题

展示问题 问题描述&#xff1a;使用el-table的fixed"right"属性后&#xff0c;如果切换tab时&#xff0c;回出现最后一列错误的问题 官网提供解决方法&#xff1a;doLayout 需要注意的事项&#xff1a;我这里是通过组件使用的table组件&#xff0c;涉及多层组件封装…...

第十二章 Python语言-大数据分析PySpark(终)

目录 一. PySpark前言介绍 二.基础准备 三.数据输入 四.数据计算 1.数据计算-map方法 2.数据计算-flatMap算子 3.数据计算-reduceByKey方法 4.数据计算-filter方法 5.数据计算-distinct方法 6.数据计算-sortBy方法 五.数据输出 1.输出Python对象 &#xff08;1&am…...

【RedisLockRegistry】分布式锁

RedisLockRegistry分布式锁 介绍 RedisLockRegistry‌是Spring框架提供的一种分布式锁机制&#xff0c;它基于Redis来实现对共享资源的保护&#xff0c;防止多个进程同时对同一资源进行修改&#xff0c;从而避免数据不一致或其他问题‌ 基本原理 RedisLockRegistry通过Redi…...

leetcode-排序

排序 面试题 01.01. 判定字符是否唯一 题目 实现一个算法&#xff0c;确定一个字符串 s 的所有字符是否全都不同。 示例 1&#xff1a; 输入: s "leetcode" 输出: false 示例 2&#xff1a; 输入: s "abc" 输出: true限制&#xff1a; 0 < len(s) &…...

AD相同网络的铜皮和导线连接不上

出现这样的情况是不是很烦恼&#xff0c;明明是相同的网络连接不上&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; 直接修改铜皮属性&#xff08;选择所有相同这个选项&#xff09; 这样就可以连接上了...

keil修改字体无效,修改字体为“微软雅黑”方法

在网上下载了微软雅黑字体&#xff0c;微软雅黑参考下载链接 结果在Edit->Configuration中找不到这个字体 这个时候可以在keil的安装目录中找到UV4/global.prop文件 用记事本打开它进行编辑&#xff0c;把字体名字改成微软雅黑 重新打开keil就发现字体成功修改了。 这个…...

【网络编程】从零开始彻底了解网络编程(三)

本篇博客给大家带来的是网络编程的知识点. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快乐顺便进步 TCP流…...

NVIDIA --- 端到端自动驾驶

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、传统驾驶模型二、NVIDIA的端到端驾驶模型1.基本模型2.自查讯向量3.通用框架 总结 前言 端到端自动驾驶指的是系统接收来自摄像头雷达和激光雷达的原始传感…...

关于在Springboot中设置时间格式问题

目录 1-设置全局时间格式1.Date类型的时间2.JDK8时间3.使Date类和JDK8时间类统统格式化时间 2-关于DateTimeFormat注解 1-设置全局时间格式 1.Date类型的时间 对于老项目来说&#xff0c;springboot中许多类使用的是Date类型的时间&#xff0c;没有用到LocalDateTime等JDK8时…...

CSRF请求伪造

该漏洞主要是关乎于用户&#xff0c;告诫用户不可乱点击链接&#xff0c;提升自我防范&#xff0c;才能不落入Hacker布置的陷阱&#xff01; 1. cookie与session 简单理解一下两者作用 1.1. &#x1f36a; Cookie&#xff1a;就像超市的会员卡 存储位置&#xff1a;你钱包里…...

(一)单机架构、应用数据分离架构、应用服务集群架构

文章目录 明确为什么要学习架构的演进单机架构什么是单机架构单机架构的模型单机架构的优缺点优点缺点 单机架构的技术案例 应用数据分离架构什么是应用数据分离架构架构模型应用数据分离架构的优缺点优点缺点 技术案例 应用服务集群架构什么是应用服务集群架构架构模型应用服务…...

Python数据分析案例72——基于股吧评论数据的情感分析和主题建模(LDA)

背景 好久没更新了&#xff0c;最近忙其他去了。最近股市波动太大&#xff0c;看了不少新闻的评论。抽空写了个股吧评论数据的LDA建模和情感分析&#xff0c;简单写到博客上来更新一下。 数据来源 上证指数(000001)股吧_上证指数怎么样_分析讨论社区— 数据来源上述网站的东…...

Git分支管理方案

成都众望智慧有限公司Git分支管理方案 采用 轻量级Git Flow 敏捷版本控制策略&#xff0c;在保证稳定性的同时提升开发效率。以下是优化后的方案&#xff1a; 1. 精简分支模型&#xff08;相比6-8人团队减少分支层级&#xff09; 分支类型作用生命周期devops生产环境代码&am…...

力扣-160.相交链表

题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返…...