代码随想录算法训练营第六十天|Day60 图论
Bellman_ford 队列优化算法(又名SPFA)
https://www.programmercarl.com/kamacoder/0094.%E5%9F%8E%E5%B8%82%E9%97%B4%E8%B4%A7%E7%89%A9%E8%BF%90%E8%BE%93I-SPFA.html
本题我们来系统讲解 Bellman_ford 队列优化算法 ,也叫SPFA算法(Shortest Path Faster Algorithm)。
SPFA的称呼来自 1994年西南交通大学段凡丁的论文,其实Bellman_ford 提出后不久 (20世纪50年代末期) 就有队列优化的版本,国际上不承认这个算法是是国内提出的。 所以国际上一般称呼 该算法为 Bellman_ford 队列优化算法(Queue improved Bellman-Ford)
大家知道以上来历,知道 SPFA 和 Bellman_ford 队列优化算法 指的都是一个算法就好。
如果大家还不够了解 Bellman_ford 算法,强烈建议按照《代码随想录》的顺序学习,否则可能看不懂下面的讲解。
大家可以发现 Bellman_ford 算法每次松弛 都是对所有边进行松弛。
但真正有效的松弛,是基于已经计算过的节点在做的松弛。
给大家举一个例子:
本图中,对所有边进行松弛,真正有效的松弛,只有松弛 边(节点1->节点2) 和 边(节点1->节点3) 。
而松弛 边(节点4->节点6) ,边(节点5->节点3)等等 都是无效的操作,因为 节点4 和 节点 5 都是没有被计算过的节点。
所以 Bellman_ford 算法 每次都是对所有边进行松弛,其实是多做了一些无用功。
只需要对 上一次松弛的时候更新过的节点作为出发节点所连接的边 进行松弛就够了。
基于以上思路,如何记录 上次松弛的时候更新过的节点呢?
用队列来记录。(其实用栈也行,对元素顺序没有要求)
#模拟过程
接下来来举例这个队列是如何工作的。
以示例给出的所有边为例:
5 6 -2 1 2 1 5 3 1 2 5 2 2 4 -3 4 6 4 1 3 51
2
3
4
5
6
7我们依然使用minDist数组来表达 起点到各个节点的最短距离,例如minDist[3] = 5 表示起点到达节点3 的最小距离为5
初始化,起点为节点1, 起点到起点的最短距离为0,所以minDist[1] 为 0。 将节点1 加入队列 (下次松弛从节点1开始)
从队列里取出节点1,松弛节点1 作为出发点连接的边(节点1 -> 节点2)和边(节点1 -> 节点3)
边:节点1 -> 节点2,权值为1 ,minDist[2] > minDist[1] + 1 ,更新 minDist[2] = minDist[1] + 1 = 0 + 1 = 1 。
边:节点1 -> 节点3,权值为5 ,minDist[3] > minDist[1] + 5,更新 minDist[3] = minDist[1] + 5 = 0 + 5 = 5。
将节点2、节点3 加入队列,如图:
从队列里取出节点2,松弛节点2 作为出发点连接的边(节点2 -> 节点4)和边(节点2 -> 节点5)
边:节点2 -> 节点4,权值为1 ,minDist[4] > minDist[2] + (-3) ,更新 minDist[4] = minDist[2] + (-3) = 1 + (-3) = -2 。
边:节点2 -> 节点5,权值为2 ,minDist[5] > minDist[2] + 2 ,更新 minDist[5] = minDist[2] + 2 = 1 + 2 = 3 。
将节点4,节点5 加入队列,如图:
从队列里出去节点3,松弛节点3 作为出发点连接的边。
因为没有从节点3作为出发点的边,所以这里就从队列里取出节点3就好,不用做其他操作,如图:
从队列中取出节点4,松弛节点4作为出发点连接的边(节点4 -> 节点6)
边:节点4 -> 节点6,权值为4 ,minDist[6] > minDist[4] + 4,更新 minDist[6] = minDist[4] + 4 = -2 + 4 = 2 。
将节点6加入队列
如图:
从队列中取出节点5,松弛节点5作为出发点连接的边(节点5 -> 节点3),边(节点5 -> 节点6)
边:节点5 -> 节点3,权值为1 ,minDist[3] > minDist[5] + 1 ,更新 minDist[3] = minDist[5] + 1 = 3 + 1 = 4
边:节点5 -> 节点6,权值为-2 ,minDist[6] > minDist[5] + (-2) ,更新 minDist[6] = minDist[5] + (-2) = 3 - 2 = 1
如图,将节点3加入队列,因为节点6已经在队列里,所以不用重复添加
所以我们在加入队列的过程可以有一个优化,用visited数组记录已经在队列里的元素,已经在队列的元素不用重复加入
从队列中取出节点6,松弛节点6 作为出发点连接的边。
节点6作为终点,没有可以出发的边。
同理从队列中取出节点3,也没有可以出发的边
所以直接从队列中取出,如图:
这样我们就完成了基于队列优化的bellman_ford的算法模拟过程。
大家可以发现 基于队列优化的算法,要比bellman_ford 算法 减少很多无用的松弛情况,特别是对于边数众多的大图 优化效果明显。
了解了大体流程,我们再看代码应该怎么写。
在上面模拟过程中,我们每次都要知道 一个节点作为出发点连接了哪些节点。
如果想方便知道这些数据,就需要使用邻接表来存储这个图,如果对于邻接表不了解的话,可以看 kama0047.参会dijkstra堆 中 图的存储 部分。
思路
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdbool.h>#define MAXN 1000 // 假设最大节点数为1000
#define MAXM 10000 // 假设最大边数为10000typedef struct Edge {int to; // 链接的节点int val; // 边的权重struct Edge* next; // 指向下一个边的指针
} Edge;typedef struct {Edge* head; // 邻接表的头
} Vertex;// 创建一个新的边
Edge* createEdge(int to, int val) {Edge* edge = (Edge*)malloc(sizeof(Edge));edge->to = to;edge->val = val;edge->next = NULL;return edge;
}int main() {int n, m, p1, p2, val;scanf("%d %d", &n, &m);Vertex graph[MAXN + 1]; // 邻接表for (int i = 1; i <= n; i++) {graph[i].head = NULL; // 初始化每个节点的头节点}// 将所有边保存起来for (int i = 0; i < m; i++) {scanf("%d %d %d", &p1, &p2, &val);Edge* edge = createEdge(p2, val);edge->next = graph[p1].head; // 插入到邻接表的头部graph[p1].head = edge;}int start = 1; // 起点int end = n; // 终点int minDist[MAXN + 1];for (int i = 1; i <= n; i++) {minDist[i] = INT_MAX; // 初始化为无穷大}minDist[start] = 0; // 起点到自身的距离为0int queue[MAXN]; // FIFO 队列int front = 0, rear = 0; // 队列的前端和后端bool isInQueue[MAXN + 1] = {false}; // 标记节点是否在队列中queue[rear++] = start; // 将起点加入队列isInQueue[start] = true;while (front < rear) {int node = queue[front++]; // 从队列中取出节点isInQueue[node] = false; // 取消标记for (Edge* edge = graph[node].head; edge != NULL; edge = edge->next) {int to = edge->to; // 获取目的节点int value = edge->val; // 获取边的权重// 松弛操作if (minDist[to] > minDist[node] + value) {minDist[to] = minDist[node] + value;if (!isInQueue[to]) { // 只有不在队列中的节点才加入queue[rear++] = to; // 加入队列isInQueue[to] = true;}}}}// 输出结果if (minDist[end] == INT_MAX) {printf("unconnected\n"); // 不能到达终点} else {printf("%d\n", minDist[end]); // 到达终点最短路径}// 释放分配的内存for (int i = 1; i <= n; i++) {Edge* edge = graph[i].head;while (edge != NULL) {Edge* temp = edge;edge = edge->next;free(temp);}}return 0;
} 学习反思
实现了使用邻接表来存储图,并利用BFS算法求解起点到终点的最短路径。
代码的主要思路如下:
- 定义了Edge结构体表示边,Vertex结构体表示节点。
- 创建了一个新的边的函数createEdge(),用于分配内存并初始化边的属性。
- 读取输入的节点数和边数,并创建邻接表graph。
- 使用for循环将所有边保存到邻接表中。
- 设置起点和终点,初始化最短路径数组minDist。
- 使用FIFO队列和BFS算法求解最短路径。
- 输出结果。
- 释放动态分配的内存。
这段代码的时间复杂度为O(n+m),其中n为节点数,m为边数。空间复杂度为O(n)。
bellman_ford之判断负权回路
https://www.programmercarl.com/kamacoder/0095.%E5%9F%8E%E5%B8%82%E9%97%B4%E8%B4%A7%E7%89%A9%E8%BF%90%E8%BE%93II.html
本题是 kama94.城市间货物运输I 延伸题目。
本题是要我们判断 负权回路,也就是图中出现环且环上的边总权值为负数。
如果在这样的图中求最短路的话, 就会在这个环里无限循环 (也是负数+负数 只会越来越小),无法求出最短路径。
所以对于 在有负权值的图中求最短路,都需要先看看这个图里有没有负权回路。
接下来我们来看 如何使用 bellman_ford 算法来判断 负权回路。
在 kama94.城市间货物运输I 中 我们讲了 bellman_ford 算法的核心就是一句话:对 所有边 进行 n-1 次松弛。 同时文中的 【拓展】部分, 我们也讲了 松弛n次以上 会怎么样?
在没有负权回路的图中,松弛 n 次以上 ,结果不会有变化。
但本题有 负权回路,如果松弛 n 次,结果就会有变化了,因为 有负权回路 就是可以无限最短路径(一直绕圈,就可以一直得到无限小的最短距离)。
那么每松弛一次,都会更新最短路径,所以结果会一直有变化。
(如果对于 bellman_ford 不了解的录友,建议详细看这里:kama94.城市间货物运输I)
以上为理论分析,接下来我们再画图举例。
我们拿题目中示例来画一个图:
图中 节点1 到 节点4 的最短路径是多少(题目中的最低运输成本) (注意边可以为负数的)
节点1 -> 节点2 -> 节点3 -> 节点4,这样的路径总成本为 -1 + 1 + 1 = 1
而图中有负权回路:
那么我们在负权回路中多绕一圈,我们的最短路径 是不是就更小了 (也就是更低的运输成本)
节点1 -> 节点2 -> 节点3 -> 节点1 -> 节点2 -> 节点3 -> 节点4,这样的路径总成本 (-1) + 1 + (-1) + (-1) + 1 + (-1) + 1 = -1
如果在负权回路多绕两圈,三圈,无穷圈,那么我们的总成本就会无限小, 如果要求最小成本的话,你会发现本题就无解了。
在 bellman_ford 算法中,松弛 n-1 次所有的边 就可以求得 起点到任何节点的最短路径,松弛 n 次以上,minDist数组(记录起到到其他节点的最短距离)中的结果也不会有改变 (如果对 bellman_ford 算法 不了解,也不知道 minDist 是什么,建议详看上篇讲解kama94.城市间货物运输I)
而本题有负权回路的情况下,一直都会有更短的最短路,所以 松弛 第n次,minDist数组 也会发生改变。
那么解决本题的 核心思路,就是在 kama94.城市间货物运输I 的基础上,再多松弛一次,看minDist数组 是否发生变化。
思路
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <stdbool.h>#define MAXN 1000 // 假设最大节点数
#define MAXM 10000 // 假设最大边数typedef struct {int from; // 边的起点int to; // 边的终点int weight; // 边的权重
} Edge;int main() {int n, m, p1, p2, val;scanf("%d %d", &n, &m);Edge edges[MAXM]; // 存储所有边for (int i = 0; i < m; i++) {scanf("%d %d %d", &p1, &p2, &val);edges[i].from = p1;edges[i].to = p2;edges[i].weight = val;}int start = 1; // 起点int end = n; // 终点// 存储从起点到每个节点的最短距离int minDist[MAXN + 1];for (int i = 1; i <= n; i++) {minDist[i] = INT_MAX; // 初始化为无穷大}minDist[start] = 0; // 起点到自身的距离为0bool flag = false; // 标记是否存在负权回路for (int i = 1; i <= n; i++) { // 松弛n次for (int j = 0; j < m; j++) {int from = edges[j].from;int to = edges[j].to;int weight = edges[j].weight;if (i < n) {// 前n-1次,进行正常的松弛if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + weight) {minDist[to] = minDist[from] + weight;}} else {// 第n次,检查是否存在负权回路if (minDist[from] != INT_MAX && minDist[to] > minDist[from] + weight) {flag = true;}}}}// 输出结果if (flag) {printf("circle\n"); // 存在负权回路} else if (minDist[end] == INT_MAX) {printf("unconnected\n"); // 不能到达终点} else {printf("%d\n", minDist[end]); // 到达终点的最短路径}return 0;
} 学习反思
代码是求解带权有向图的单源最短路径问题,使用了Bellman-Ford算法。
算法的思想是从起点开始,进行n-1次松弛操作,每次松弛都尝试通过当前节点更新到达其他节点的最短路径长度。最后,再进行一次松弛操作,如果还能通过该操作更新最短路径,则说明存在负权回路。
具体实现上,通过一个长度为n的数组minDist[]来记录从起点到各个节点的最短路径长度,初始化为INT_MAX,表示无穷大。
在每次松弛操作中,遍历所有边,如果从起点到当前边的起点的路径长度不为无穷大,并且通过该边可以更新到达当前边终点的最短路径长度,则更新minDist[]数组的值。
在最后一次松弛操作中,如果还可以更新最短路径,则说明存在负权回路。
最后根据minDist[end]的值来判断结果,如果为INT_MAX,则说明无法到达终点;如果为负数,则说明存在负权回路;否则,表示到达终点的最短路径长度。
这段代码的时间复杂度为O(nm),其中n为节点数,m为边数。
bellman_ford之单源有限最短路
https://www.programmercarl.com/kamacoder/0096.%E5%9F%8E%E5%B8%82%E9%97%B4%E8%B4%A7%E7%89%A9%E8%BF%90%E8%BE%93III.html
思路
本题为单源有限最短路问题,同样是 kama94.城市间货物运输I 延伸题目。
注意题目中描述是 最多经过 k 个城市的条件下,而不是一定经过k个城市,也可以经过的城市数量比k小,但要最短的路径。
在 kama94.城市间货物运输I 中我们讲了:对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离。
节点数量为n,起点到终点,最多是 n-1 条边相连。 那么对所有边松弛 n-1 次 就一定能得到 起点到达 终点的最短距离。
(如果对以上讲解看不懂,建议详看 kama94.城市间货物运输I )
本题是最多经过 k 个城市, 那么是 k + 1条边相连的节点。 这里可能有录友想不懂为什么是k + 1,来看这个图:
图中,节点1 最多已经经过2个节点 到达节点4,那么中间是有多少条边呢,是 3 条边对吧。
所以本题就是求:起点最多经过k + 1 条边到达终点的最短距离。
对所有边松弛一次,相当于计算 起点到达 与起点一条边相连的节点 的最短距离,那么对所有边松弛 k + 1次,就是求 起点到达 与起点k + 1条边相连的节点的 最短距离。
注意: 本题是 kama94.城市间货物运输I 的拓展题,如果对 bellman_ford 没有深入了解,强烈建议先看 kama94.城市间货物运输I 再做本题。
思路
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>#define MAXN 1000 // 假设最大节点数
#define MAXM 10000 // 假设最大边数typedef struct {int from; // 边的起点int to; // 边的终点int weight; // 边的权重
} Edge;int main() {int src, dst, k, p1, p2, val, m, n;// 读取节点数和边数scanf("%d %d", &n, &m);Edge edges[MAXM]; // 用于存储所有边for (int i = 0; i < m; i++) {scanf("%d %d %d", &p1, &p2, &val);edges[i].from = p1;edges[i].to = p2;edges[i].weight = val;}// 读取源节点、目标节点和最大边数kscanf("%d %d %d", &src, &dst, &k);// 存储从源节点到每个节点的最短距离int minDist[MAXN + 1];for (int i = 1; i <= n; i++) {minDist[i] = INT_MAX; // 初始化为无穷大}minDist[src] = 0; // 源节点到自身的距离为0// 松弛k + 1次for (int i = 1; i <= k + 1; i++) {int tempDist[MAXN + 1]; // 用于存储当前迭代的最短距离for (int j = 1; j <= n; j++) {tempDist[j] = minDist[j]; // 初始化临时数组}// 遍历所有边进行松弛for (int j = 0; j < m; j++) {int from = edges[j].from;int to = edges[j].to;int weight = edges[j].weight;if (tempDist[from] != INT_MAX && tempDist[to] > tempDist[from] + weight) {tempDist[to] = tempDist[from] + weight; // 更新最短路径}}// 将当前迭代的结果更新到minDistfor (int j = 1; j <= n; j++) {minDist[j] = tempDist[j];}}// 输出结果if (minDist[dst] == INT_MAX) {printf("unreachable\n"); // 不能到达目标节点} else {printf("%d\n", minDist[dst]); // 到达目标节点的最短路径}return 0;
} 学习反思
实现了求解单源最短路径问题的算法,采用了Bellman-Ford算法的思想。
首先,通过scanf函数读入节点数n和边数m。
然后,定义了一个结构体Edge用于存储边的信息,包括边的起点、终点和权重。
接下来,使用for循环读入m条边的信息,并将其存储到edges数组中。
再次使用scanf函数读入源节点src、目标节点dst和最大边数k。
然后,定义了一个数组minDist用于存储从源节点到每个节点的最短距离,初始值为无穷大。
接下来,进行k + 1次松弛操作,每次遍历所有边,如果发现存在从from节点到to节点的距离更短的路径,则更新最短距离。
最后,判断到达目标节点的最短路径是否存在,如果不存在则输出"unreachable",否则输出最短路径的值。
该算法的时间复杂度为O(k * m),其中k为最大边数,m为边数。
相关文章:
代码随想录算法训练营第六十天|Day60 图论
Bellman_ford 队列优化算法(又名SPFA) https://www.programmercarl.com/kamacoder/0094.%E5%9F%8E%E5%B8%82%E9%97%B4%E8%B4%A7%E7%89%A9%E8%BF%90%E8%BE%93I-SPFA.html 本题我们来系统讲解 Bellman_ford 队列优化算法 ,也叫SPFA算法…...
在嵌入式Linux下如何用QT开发UI
在嵌入式 Linux 环境下使用 Qt 开发用户界面 (UI) 是一个常见的选择。Qt 提供了丰富的功能、跨平台支持以及优秀的图形界面开发能力,非常适合用于嵌入式系统。以下是开发流程的详细步骤: 1. 准备开发环境 硬件环境 一块运行嵌入式 Linux 的开发板&…...
【JavaScript】Promise详解
Promise 是 JavaScript 中处理异步操作的一种强大机制。它提供了一种更清晰、更可控的方式来处理异步代码,避免了回调地狱(callback hell)和复杂的错误处理。 基本概念 状态: Pending:初始状态,既不是成功…...
1062 Talent and Virtue
About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about peoples talent and virtue. According to his theory, a man being outstanding in both talent and virtue must be a "sage(圣人)"…...
C++《二叉搜索树》
在初阶数据结构中我学习了树基础的概念以及了解了顺序结构的二叉树——堆和链式结构二叉树该如何实现,那么接下来我们将进一步的学习二叉树,在此会先后学习到二叉搜索树、AVL树、红黑树;通过这些的学习将让我们更易于理解后面set、map、哈希等…...
机器学习-神经网络(BP神经网络前向和反向传播推导)
1.1 神经元模型 神经网络(neural networks)方面的研究很早就已出现,今天“神经网络”已是一个相当大的、多学科交叉的学科领域.各相关学科对神经网络的定义多种多样,本书采用目前使用得最广泛的一种,即“神经网络是由具有适应性的简单单元组成的广泛并行互连的网络,它的组织能够…...
基于智能物联网关的车辆超重AI检测应用
超重超载是严重的交通违法行为,超重超载车辆的交通安全风险极高,像是一颗行走的“不定时炸弹”,威胁着社会公众的安全。但总有一些人受到利益驱使,使超重超载的违法违规行为时有发生。 随着物联网和AI技术的发展,针对预…...
记录pbootcms提示:登录失败:表单提交校验失败,请刷新后重试的解决办法
问题描述 pbootcms后台登录的时候提示“登录失败:表单提交校验失败,请刷新后重试!” 解决办法 删除runtime目录,或尝试切换PHP版本,选择7.3或5.6一般就能解决了。...
【JavaScript】同步异步详解
同步和异步是编程中处理任务执行顺序的两种不同方式。理解这两种概念对于编写高效和响应式的应用程序至关重要。 同步(Synchronous) 定义:同步操作是指一个任务必须在下一个任务开始之前完成。换句话说,代码按顺序执行ÿ…...
vue 使用el-button 如何实现多个button 单选
在 Vue 中,如果你想要实现多个 el-button 按钮的 单选(即只能选择一个按钮),可以通过绑定 v-model 或使用事件来处理按钮的选中状态。 下面是两种实现方式,分别使用 v-model 和事件监听来实现单选按钮效果:…...
HarmonyOS-初级(二)
文章目录 应用程序框架UIAbilityArkUI框架 🏡作者主页:点击! 🤖HarmonyOS专栏:点击! ⏰️创作时间:2024年11月28日13点10分 应用程序框架 应用程序框架可以被看做是应用模型的一种实现方式。 …...
Unity开启外部EXE程序
Unity开启外部EXE using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading.Tasks; using UnityEditor; using UnityEngine;public class Unity_OpenExe : Mono…...
CTF之密码学(埃特巴什码 )
一、基本原理 埃特巴什码的原理是:字母表中的最后一个字母代表第一个字母,倒数第二个字母代表第二个字母,以此类推。在罗马字母表中,对应关系如下: 常文(明文):A B C D E F G H I …...
深入解析 PyTorch 的 torch.load() 函数:用法、参数与实际应用示例
深入解析 PyTorch 的 torch.load() 函数:用法、参数与实际应用示例 函数 torch.load() 是一个在PyTorch中用于加载通过 torch.save() 保存的序列化对象的核心功能。这个函数广泛应用于加载预训练模型、模型的状态字典(state dictionaries)、…...
ros2键盘实现车辆: 简单的油门_刹车_挡位_前后左右移动控制
参考: ROS python 实现键盘控制 底盘移动 https://blog.csdn.net/u011326325/article/details/131609340游戏手柄控制 1.背景与需求 1.之前实现过 键盘控制 底盘移动的程序, 底盘是线速度控制, 效果还不错. 2.新的底盘 只支持油门控制, 使用线速度控制问题比较多, 和底盘适配…...
ubuntu安装chrome无法打开问题
如果在ubuntu安装chrome后,点击chrome打开没反应,可以先试着在terminal上用命令打开 google-chrome 如果运行命令显示 Chrome has locked the profile so that it doesnt get corrupted. If you are sure no other processes are using this profile…...
CTF-RE 从0到N:Chacha20逆向实战 2024 强网杯青少年专项赛 EnterGame WP (END)
只想解题的看最后就好了,前面是算法分析 Chacha20 c语言是如何利用逻辑运算符拆分变量和合并的 通过百度网盘分享的文件:EnterGame_9acdc7c33f85832082adc6a4e... 链接:https://pan.baidu.com/s/182SRj2Xemo63PCoaLNUsRQ?pwd1111 提取码:1…...
vue3 ajax获取json数组排序举例
使用axios获取接口数据 可以在代码中安装axios包,并写入到package.json文件: npm install axios -S接口调用代码举例如下: const fetchScore async () > {try {const res await axios.get(http://127.0.0.1:8000/score/${userInput.v…...
web安全之信息收集
在信息收集中,最主要是就是收集服务器的配置信息和网站的敏感信息,其中包括域名及子域名信息,目标网站系统,CMS指纹,目标网站真实IP,开放端口等。换句话说,只要是与目标网站相关的信息,我们都应该去尽量搜集。 1.1收集域名信息 知道目标的域名之后,获取域名的注册信…...
报错:java: 无法访问org.springframework.boot.SpringApplication
idea报错内容: java: 无法访问org.springframework.boot.SpringApplication 报错原因: <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)
引言 在嵌入式系统中,用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例,介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单,执行相应操作,并提供平滑的滚动动画效果。 本文设计了一个…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
结构化文件管理实战:实现目录自动创建与归类
手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题,进而引发后续程序异常。使用工具进行标准化操作,能有效降低出错概率。 需要快速整理大量文件的技术用户而言,这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB,…...
SQLSERVER-DB操作记录
在SQL Server中,将查询结果放入一张新表可以通过几种方法实现。 方法1:使用SELECT INTO语句 SELECT INTO 语句可以直接将查询结果作为一个新表创建出来。这个新表的结构(包括列名和数据类型)将与查询结果匹配。 SELECT * INTO 新…...
Web APIS Day01
1.声明变量const优先 那为什么一开始前面就不能用const呢,接下来看几个例子: 下面这张为什么可以用const呢?因为复杂数据的引用地址没变,数组还是数组,只是添加了个元素,本质没变,所以可以用con…...










