Dijkstra算法,你的最短路来了!

录友们,最近我在图论方面已经开始更最短路系列了,讲好最短路问题,其实也是很难的,本篇我仅仅是讲了朴素版Dijkstra,但就写了将近1w字,画了二十张图。学算法易,讲清楚难!

题目链接

【题目描述】

小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。

小明的起点是第一个车站,终点是最后一个车站。然而,途中的各个车站之间的道路状况、交通拥堵程度以及可能的自然因素(如天气变化)等不同,这些因素都会影响每条路径的通行时间。

小明希望能选择一条花费时间最少的路线,以确保他能够尽快到达目的地。

【输入描述】

第一行包含两个正整数,第一个正整数 N 表示一共有 N 个公共汽车站,第二个正整数 M 表示有 M 条公路。

接下来为 M 行,每行包括三个整数,S、E 和 V,代表了从 S 车站可以单向直达 E 车站,并且需要花费 V 单位的时间。

【输出描述】

输出一个整数,代表小明在途中和其他科学家和科研团队交流所花费的最少时间。

输入示例

7 9
1 2 1
1 3 4
2 3 2
2 4 5
3 4 2
4 5 3
2 6 4
5 7 4
6 7 9

输出示例:12

【提示信息】

能够到达的情况:

如下图所示,起始车站为 1 号车站,终点车站为 7 号车站,绿色路线为最短的路线,路线总长度为 12,则输出 12。

不能到达的情况:

如下图所示,当从起始车站不能到达终点车站时,则输出 -1。

数据范围:

1 <= N <= 500;
1 <= M <= 5000;

思路

本题就是求最短路,最短路是图论中的经典问题即:给出一个有向图,一个起点,一个终点,问起点到终点的最短路径。

接下来,我们来详细讲解最短路算法中的 dijkstra 算法。

dijkstra算法:在有权图(权值非负数)中求从起点到其他节点的最短路径算法。

需要注意两点:

  • dijkstra 算法可以同时求 起点到所有节点的最短路径
  • 权值不能为负数

(这两点后面我们会讲到)

如本题示例中的图:

起点(节点1)到终点(节点7) 的最短路径是 图中 标记绿线的部分。

最短路径的权值为12。

其实 dijkstra 算法 和 我们之前讲解的prim算法思路非常接近,如果大家认真学过prim算法,那么理解 Dijkstra 算法会相对容易很多。(这也是我要先讲prim再讲dijkstra的原因)

dijkstra 算法 同样是贪心的思路,不断寻找距离 源点最近的没有访问过的节点。

这里我也给出 dijkstra三部曲

  1. 第一步,选源点到哪个节点近且该节点未被访问过
  2. 第二步,该最近节点被标记访问过
  3. 第三步,更新非访问节点到源点的距离(即更新minDist数组)

大家此时已经会发现,这和prim算法 怎么这么像呢。

我在prim算法讲解中也给出了三部曲。 prim 和 dijkstra 确实很像,思路也是类似的,这一点我在后面还会详细来讲。

在dijkstra算法中,同样有一个数组很重要,起名为:minDist。

minDist数组 用来记录 每一个节点距离源点的最小距离

理解这一点很重要,也是理解 dijkstra 算法的核心所在。

大家现在看着可能有点懵,不知道什么意思。

没关系,先让大家有一个印象,对理解后面讲解有帮助。

我们先来画图看一下 dijkstra 的工作过程,以本题示例为例: (以下为朴素版dijkstra的思路)

示例中节点编号是从1开始,所以为了让大家看的不晕,minDist数组下标我也从 1 开始计数,下标0 就不使用了,这样 下标和节点标号就可以对应上了,避免大家搞混

朴素版dijkstra

模拟过程


0、初始化

minDist数组数值初始化为int最大值。

这里在强点一下 minDist数组的含义:记录所有节点到源点的最短路径,那么初始化的时候就应该初始为最大值,这样才能在后续出现最短路径的时候及时更新。

(图中,max 表示默认值,节点0 不做处理,统一从下标1 开始计算,这样下标和节点数值统一, 方便大家理解,避免搞混)

源点(节点1) 到自己的距离为0,所以 minDist[1] = 0

此时所有节点都没有被访问过,所以 visited数组都为0


以下为dijkstra 三部曲

1、选源点到哪个节点近且该节点未被访问过

源点距离源点最近,距离为0,且未被访问。

2、该最近节点被标记访问过

标记源点访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

更新 minDist数组,即:源点(节点1) 到 节点2 和 节点3的距离。

  • 源点到节点2的最短距离为1,小于原minDist[2]的数值max,更新minDist[2] = 1
  • 源点到节点3的最短距离为4,小于原minDist[3]的数值max,更新minDist[4] = 4

可能有录友问:为啥和 minDist[2] 比较?

再强调一下 minDist[2] 的含义,它表示源点到节点2的最短距离,那么目前我们得到了 源点到节点2的最短距离为1,小于默认值max,所以更新。 minDist[3]的更新同理


1、选源点到哪个节点近且该节点未被访问过

未访问过的节点中,源点到节点2距离最近,选节点2

2、该最近节点被标记访问过

节点2被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

更新 minDist数组,即:源点(节点1) 到 节点6 、 节点3 和 节点4的距离。

为什么更新这些节点呢? 怎么不更新其他节点呢

因为 源点(节点1)通过 已经计算过的节点(节点2) 可以链接到的节点 有 节点3,节点4和节点6.

更新 minDist数组:

  • 源点到节点6的最短距离为5,小于原minDist[6]的数值max,更新minDist[6] = 5
  • 源点到节点3的最短距离为3,小于原minDist[3]的数值4,更新minDist[3] = 3
  • 源点到节点4的最短距离为6,小于原minDist[4]的数值max,更新minDist[4] = 6

1、选源点到哪个节点近且该节点未被访问过

未访问过的节点中,源点距离哪些节点最近,怎么算的?

其实就是看 minDist数组里的数值,minDist 记录了 源点到所有节点的最近距离,结合visited数组筛选出未访问的节点就好。

从 上面的图,或者 从minDist数组中,我们都能看出 未访问过的节点中,源点(节点1)到节点3距离最近。

2、该最近节点被标记访问过

节点3被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

由于节点3的加入,那么源点可以有新的路径链接到节点4 所以更新minDist数组:

更新 minDist数组:

  • 源点到节点4的最短距离为5,小于原minDist[4]的数值6,更新minDist[4] = 5

1、选源点到哪个节点近且该节点未被访问过

距离源点最近且没有被访问过的节点,有节点4 和 节点6,距离源点距离都是 5 (minDist[4] = 5,minDist[6] = 5) ,选哪个节点都可以。

2、该最近节点被标记访问过

节点4被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

由于节点4的加入,那么源点可以链接到节点5 所以更新minDist数组:

  • 源点到节点5的最短距离为8,小于原minDist[5]的数值max,更新minDist[5] = 8

1、选源点到哪个节点近且该节点未被访问过

距离源点最近且没有被访问过的节点,是节点6,距离源点距离是 5 (minDist[6] = 5)

2、该最近节点被标记访问过

节点6 被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

由于节点6的加入,那么源点可以链接到节点7 所以 更新minDist数组:

  • 源点到节点7的最短距离为14,小于原minDist[7]的数值max,更新minDist[7] = 14

1、选源点到哪个节点近且该节点未被访问过

距离源点最近且没有被访问过的节点,是节点5,距离源点距离是 8 (minDist[5] = 8)

2、该最近节点被标记访问过

节点5 被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

由于节点5的加入,那么源点有新的路径可以链接到节点7 所以 更新minDist数组:

  • 源点到节点7的最短距离为12,小于原minDist[7]的数值14,更新minDist[7] = 12

1、选源点到哪个节点近且该节点未被访问过

距离源点最近且没有被访问过的节点,是节点7(终点),距离源点距离是 12 (minDist[7] = 12)

2、该最近节点被标记访问过

节点7 被标记访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

节点7加入,但节点7到节点7的距离为0,所以 不用更新minDist数组


最后我们要求起点(节点1) 到终点 (节点7)的距离。

再来回顾一下minDist数组的含义:记录 每一个节点距离源点的最小距离。

那么起到(节点1)到终点(节点7)的最短距离就是 minDist[7] ,按上面举例讲解来说,minDist[7] = 12,节点1 到节点7的最短路径为 12。

路径如图:

在上面的讲解中,每一步 我都是按照 dijkstra 三部曲来讲解的,理解了这三部曲,代码也就好懂的。

代码实现

本题代码如下,里面的 三部曲 我都做了注释,大家按照我上面的讲解 来看如下代码:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {int n, m, p1, p2, val;cin >> n >> m;vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));for(int i = 0; i < m; i++){cin >> p1 >> p2 >> val;grid[p1][p2] = val;}int start = 1;int end = n;// 存储从源点到每个节点的最短距离std::vector<int> minDist(n + 1, INT_MAX);// 记录顶点是否被访问过std::vector<bool> visited(n + 1, false);minDist[start] = 0;  // 起始点到自身的距离为0for (int i = 1; i <= n; i++) { // 遍历所有节点int minVal = INT_MAX;int cur = 1;// 1、选距离源点最近且未访问过的节点for (int v = 1; v <= n; ++v) {if (!visited[v] && minDist[v] < minVal) {minVal = minDist[v];cur = v;}}visited[cur] = true;  // 2、标记该节点已被访问// 3、第三步,更新非访问节点到源点的距离(即更新minDist数组)for (int v = 1; v <= n; v++) {if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {minDist[v] = minDist[cur] + grid[cur][v];}}}if (minDist[end] == INT_MAX) cout << -1 << endl; // 不能到达终点else cout << minDist[end] << endl; // 到达终点最短路径}
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(n^2)

debug方法

写这种题目难免会有各种各样的问题,我们如何发现自己的代码是否有问题呢?

最好的方式就是打日志,本题的话,就是将 minDist 数组打印出来,就可以很明显发现 哪里出问题了。

每次选择节点后,minDist数组的变化是否符合预期 ,是否和我上面讲的逻辑是对应的。

例如本题,如果想debug的话,打印日志可以这样写:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {int n, m, p1, p2, val;cin >> n >> m;vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));for(int i = 0; i < m; i++){cin >> p1 >> p2 >> val;grid[p1][p2] = val;}int start = 1;int end = n;std::vector<int> minDist(n + 1, INT_MAX);std::vector<bool> visited(n + 1, false);minDist[start] = 0;for (int i = 1; i <= n; i++) {int minVal = INT_MAX;int cur = 1;for (int v = 1; v <= n; ++v) {if (!visited[v] && minDist[v] < minVal) {minVal = minDist[v];cur = v;}}visited[cur] = true;for (int v = 1; v <= n; v++) {if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {minDist[v] = minDist[cur] + grid[cur][v];}}// 打印日志:cout << "select:" << cur << endl;for (int v = 1; v <= n; v++) cout <<  v << ":" << minDist[v] << " ";cout << endl << endl;;}if (minDist[end] == INT_MAX) cout << -1 << endl;else cout << minDist[end] << endl;}

打印后的结果:

select:1
1:0 2:1 3:4 4:2147483647 5:2147483647 6:2147483647 7:2147483647select:2
1:0 2:1 3:3 4:6 5:2147483647 6:5 7:2147483647select:3
1:0 2:1 3:3 4:5 5:2147483647 6:5 7:2147483647select:4
1:0 2:1 3:3 4:5 5:8 6:5 7:2147483647select:6
1:0 2:1 3:3 4:5 5:8 6:5 7:14select:5
1:0 2:1 3:3 4:5 5:8 6:5 7:12select:7
1:0 2:1 3:3 4:5 5:8 6:5 7:12

打印日志可以和上面我讲解的过程进行对比,每一步的结果是完全对应的。

所以如果大家如果代码有问题,打日志来debug是最好的方法

如何求路径

如果题目要求把最短路的路径打印出来,应该怎么办呢?

这里还是有一些“坑”的,本题打印路径和 prim 打印路径是一样的,我在 prim算法精讲 【拓展】中 已经详细讲解了。

在这里就不再赘述。

打印路径只需要添加 几行代码, 打印路径的代码我都加上的日志,如下:

#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {int n, m, p1, p2, val;cin >> n >> m;vector<vector<int>> grid(n + 1, vector<int>(n + 1, INT_MAX));for(int i = 0; i < m; i++){cin >> p1 >> p2 >> val;grid[p1][p2] = val;}int start = 1;int end = n;std::vector<int> minDist(n + 1, INT_MAX);std::vector<bool> visited(n + 1, false);minDist[start] = 0;//加上初始化vector<int> parent(n + 1, -1);for (int i = 1; i <= n; i++) {int minVal = INT_MAX;int cur = 1;for (int v = 1; v <= n; ++v) {if (!visited[v] && minDist[v] < minVal) {minVal = minDist[v];cur = v;}}visited[cur] = true;for (int v = 1; v <= n; v++) {if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {minDist[v] = minDist[cur] + grid[cur][v];parent[v] = cur; // 记录边}}}// 输出最短情况for (int i = 1; i <= n; i++) {cout << parent[i] << "->" << i << endl;}
}

打印结果:

-1->1
1->2
2->3
3->4
4->5
2->6
5->7

对应如图:

出现负数

如果图中边的权值为负数,dijkstra 还合适吗?

看一下这个图: (有负权值)

节点1 到 节点5 的最短路径 应该是 节点1 -> 节点2 -> 节点3 -> 节点4 -> 节点5

那我们来看dijkstra 求解的路径是什么样的,继续dijkstra 三部曲来模拟 :(dijkstra模拟过程上面已经详细讲过,以下只模拟重要过程,例如如何初始化就省略讲解了)


初始化:


1、选源点到哪个节点近且该节点未被访问过

源点距离源点最近,距离为0,且未被访问。

2、该最近节点被标记访问过

标记源点访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

更新 minDist数组,即:源点(节点1) 到 节点2 和 节点3的距离。

  • 源点到节点2的最短距离为100,小于原minDist[2]的数值max,更新minDist[2] = 100
  • 源点到节点3的最短距离为1,小于原minDist[3]的数值max,更新minDist[4] = 1

1、选源点到哪个节点近且该节点未被访问过

源点距离节点3最近,距离为1,且未被访问。

2、该最近节点被标记访问过

标记节点3访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

由于节点3的加入,那么源点可以有新的路径链接到节点4 所以更新minDist数组:

  • 源点到节点4的最短距离为2,小于原minDist[4]的数值max,更新minDist[4] = 2

1、选源点到哪个节点近且该节点未被访问过

源点距离节点4最近,距离为2,且未被访问。

2、该最近节点被标记访问过

标记节点4访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

由于节点4的加入,那么源点可以有新的路径链接到节点5 所以更新minDist数组:

  • 源点到节点5的最短距离为3,小于原minDist[5]的数值max,更新minDist[5] = 5

1、选源点到哪个节点近且该节点未被访问过

源点距离节点5最近,距离为3,且未被访问。

2、该最近节点被标记访问过

标记节点5访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:

节点5的加入,而节点5 没有链接其他节点, 所以不用更新minDist数组,仅标记节点5被访问过了


1、选源点到哪个节点近且该节点未被访问过

源点距离节点2最近,距离为100,且未被访问。

2、该最近节点被标记访问过

标记节点2访问过

3、更新非访问节点到源点的距离(即更新minDist数组) ,如图:


至此dijkstra的模拟过程就结束了,根据最后的minDist数组,我们求 节点1 到 节点5 的最短路径的权值总和为 3,路径: 节点1 -> 节点3 -> 节点4 -> 节点5

通过以上的过程模拟,我们可以发现 之所以 没有走有负权值的最短路径 是因为 在 访问 节点 2 的时候,节点 3 已经访问过了,就不会再更新了。

那有录友可能会想: 我可以改代码逻辑啊,访问过的节点,也让它继续访问不就好了?

那么访问过的节点还能继续访问会不会有死循环的出现呢?控制逻辑不让其死循环?那特殊情况自己能都想清楚吗?(可以试试,实践出真知)

对于负权值的出现,大家可以针对某一个场景 不断去修改 dijkstra 的代码,但最终会发现只是 拆了东墙补西墙,对dijkstra的补充逻辑只能满足某特定场景最短路求解。

对于求解带有负权值的最短路问题,可以使用 Bellman-Ford 算法 ,我在后序会详细讲解。

dijkstra与prim算法的区别

这里再次提示,需要先看我的 prim算法精讲 ,否则可能不知道我下面讲的是什么。

大家可以发现 dijkstra的代码看上去 怎么和 prim算法这么像呢。

其实代码大体不差,唯一区别在 三部曲中的 第三步: 更新minDist数组

因为prim是求 非访问节点到最小生成树的最小距离,而 dijkstra是求 非访问节点到源点的最小距离

prim 更新 minDist数组的写法:

for (int j = 1; j <= v; j++) {if (!isInTree[j] && grid[cur][j] < minDist[j]) {minDist[j] = grid[cur][j];}
}

因为 minDist表示 节点到最小生成树的最小距离,所以 新节点cur的加入,只需要 使用 grid[cur][j] ,grid[cur][j] 就表示 cur 加入生成树后,生成树到 节点j 的距离。

dijkstra 更新 minDist数组的写法:

for (int v = 1; v <= n; v++) {if (!visited[v] && grid[cur][v] != INT_MAX && minDist[cur] + grid[cur][v] < minDist[v]) {minDist[v] = minDist[cur] + grid[cur][v];}
}

因为 minDist表示 节点到源点的最小距离,所以 新节点 cur 的加入,需要使用 源点到cur的距离 (minDist[cur]) + cur 到 节点 v 的距离 (grid[cur][v]),才是 源点到节点v的距离。

此时大家可能不禁要想 prim算法 可以有负权值吗?

当然可以!

录友们可以自己思考思考一下,这是为什么?

这里我提示一下:prim算法只需要将节点以最小权值和链接在一起,不涉及到单一路径。

总结

本篇,我们深入讲解的dijkstra算法,详细模拟其工作的流程。

这里我给出了 dijkstra 三部曲 来 帮助大家理解 该算法,不至于 每次写 dijkstra 都是黑盒操作,没有框架没有章法。

在给出的代码中,我也按照三部曲的逻辑来给大家注释,只要理解这三部曲,即使 过段时间 对 dijkstra 算法有些遗忘,依然可以写出一个框架出来,然后再去调试细节。

对于图论算法,一般代码都比较长,很难写出代码直接可以提交通过,都需要一个debug的过程,所以 学习如何debug 非常重要

这也是我为什么 在本文中 单独用来讲解 debug方法。

本题求的是最短路径和是多少,同时我们也要掌握 如何把最短路径打印出来

我还写了大篇幅来讲解 负权值的情况, 只有画图带大家一步一步去 看 出现负权值 dijkstra的求解过程,才能帮助大家理解,问题出在哪里。

如果我直接讲:是因为访问过的节点 不能再访问,导致错过真正的最短路,我相信大家都不知道我在说啥。

最后我还讲解了 dijkstra 和 prim 算法的 相同 与 不同之处, 我在图论的讲解安排中 先讲 prim算法 再讲 dijkstra 是有目的的, 理解这两个算法的相同与不同之处 有助于大家学习的更深入

而不是 学了 dijkstra 就只看 dijkstra, 算法之间 都是有联系的,多去思考 算法之间的相互联系,会帮助大家思考的更深入,掌握的更彻底。

本篇写了这么长,我也只讲解了 朴素版dijkstra,关于 堆优化dijkstra,我会在下一篇再来给大家详细讲解

加油

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

C语言(static和extern)

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

Midjourney是什么?Midjourney怎么用?怎么注册Midjourney账号?国内怎么使用Midjourney?多人合租Midjourney拼车

Midjourney是什么 OpenAI发布的ChatGPT4引领了聊天机器人的竞争浪潮&#xff0c;随后谷歌推出了自己的AI聊天机器人Bard&#xff0c;紧接着微软推出了Bing Chat&#xff0c;百度也推出了文心一言&#xff0c;这些聊天机器人的推出&#xff0c;标志着对话式AI技术已经达到了一个…

TCP相关问题总结

文章目录 TCP连接建立过程1. TCP三次握手2. TCP四次挥手3. TCP为什么是三次握手4. TCP为什么是四次挥手 TCP流量控制TCP拥塞控制1. 为什么需要拥塞控制2. 控制手段 TCP连接建立过程中出现丢包 TCP连接建立过程 1. TCP三次握手 首先client端发出连接请求&#xff0c;并且请求同…

RAG:智能图书馆员引领AI的知识之旅

想象一下&#xff0c;你是一个法官&#xff0c;面对一个复杂的案件&#xff0c;你需要查阅大量的法律文献来找到相关的案例和法律条文。在AI的世界里&#xff0c;也有一个类似的“法官”——大型语言模型&#xff08;LLMs&#xff09;。它们能够回答各种问题&#xff0c;但有时…

【大模型系列】预训练

数据 数据预处理 预处理流程&#xff1a; 原始语料库—>质量过滤&#xff08;语种过滤、统计过滤、关键词过滤、分类器过滤&#xff09;—>敏感内容过滤&#xff08;有毒内容、隐私内容PII&#xff09;—>数据去重&#xff08;句子级别、文档级别、数据集级别&#…

文件读取和写入

1、with open 和 open close 的对比 with open 的优点 1、自动关闭文件&#xff1a;with 语句会在代码块执行完毕后自动关闭文件&#xff0c;无需显式调用 close() 方法。 2、异常安全&#xff1a;如果在代码块中发生异常&#xff0c;with 语句仍然会确保文件被正确关闭。 3、…

2024大连化工展|中国(大连)国际化工产业展览会

2024大连化工展|中国&#xff08;大连&#xff09;国际化工产业展览会 时间&#xff1a;2024年7月24-26日 地点&#xff1a;大连世界博览广场 大会主题&#xff1a;共谋新发展 共创新机遇 大连作为东北地区最具活力和发展潜力的城市&#xff0c;同时也是我国重要的化工产业基…

上网行为管理软件怎么选 三款好用的上网行为管理软件

上网行为管理软件怎么选 三款好用的上网行为管理软件 一款优秀的上网行为管理软件可以满足企业的多种需求&#xff0c;帮助企业有效监督员工的行为&#xff0c;提升工作效率和企业效益&#xff0c;但是这些软件差异较大&#xff0c;选择的时候需要考虑这些因素。 1、明确需求 …

(2024)Visual Studio的介绍、安装与使用

Visual Studio介绍 1.Visual Studio是什么&#xff1f; Visual Studio是微软公司推出的一款开发工具包系列产品&#xff0c;它是一个基本完整的开发工具集&#xff0c;为软件开发者提供了整个软件生命周期中所需的大部分工具。 2.Visual Studio的定义 Visual Studio是美国微软公…

kerberos:介绍

文章目录 一、介绍二、kerberos框架1、名词解释2、框架 三、优缺点四、其他认证机制1、SSL2、OAuth3、LDAP 一、介绍 Kerberos是一种计算机网络授权协议&#xff0c;主要用于在非安全网络环境中对个人通信进行安全的身份认证。这个协议由麻省理工学院&#xff08;MIT&#xff…

C语言 | Leetcode C语言题解之第40题组合总和II

题目&#xff1a; 题解&#xff1a; int** ans; int* ansColumnSizes; int ansSize;int* sequence; int sequenceSize;int** freq; int freqSize;void dfs(int pos, int rest) {if (rest 0) {int* tmp malloc(sizeof(int) * sequenceSize);memcpy(tmp, sequence, sizeof(int…

【QT学习】9.绘图,三种贴图,贴图的转换,不规则贴图(透明泡泡)

一。绘图的解释 Qt 中提供了强大的 2D 绘图系统&#xff0c;可以使用相同的 API 在屏幕和绘图设备上进行绘制&#xff0c;它主要基于QPainter、QPaintDevice 和 QPaintEngine 这三个类。 QPainter 用于执行绘图操作&#xff0c;其提供的 API 在 GUI 或 QImage、QOpenGLPaintDev…

Jmeter04:关联

1 Jmeter组件&#xff1a;关联 概括&#xff1a;2个请求之间不是独立的&#xff0c;一个请求响应的结果是作为另一个请求提交的数据&#xff0c;存在数据交互 1.1 是什么&#xff1f; 就是一个请求的结果是另一个请求提交的数据&#xff0c;二者不再是独立 1.2 为什么&#x…

docker容器内彻底移除iptables服务的实现方法

背景 我创建的容器使用的是centos6的标准镜像&#xff0c;所以内置了iptables服务。容器启动后iptables服务默认就启动了。iptables设置的规则默认是所有流量都无法通行。而对于服务器的管理使用的是宿主机的防火墙。这样就导致在实现用iptables动态给容器添加端口映射时不成功…

RocketMQ快速入门:group、topic、queue、tag等基本概念(四)

0. 引言 上一节&#xff0c;我们说明了rocketmq中的4个核心组成以及他们之间的工作关系。但其中穿插的topic, queue等概念&#xff0c;如果未接触过mq的同学可能会有些迷糊&#xff0c;所以本节&#xff0c;我们重点针对rocketmq中的基本概念进行讲解&#xff0c;之前学习过其…

中北大学软件学院操作系统实验二进程调度算法

实验时间 2024年 4 月13日14时至16时 学时数 2 1.实验名称 实验二进程调度算法 2.实验目的 (1)加深对进程的概念及进程调度算法的理解&#xff1b; (2)在了解和掌握进程调度算法的基础上&#xff0c;编制进程调度算法通用程序&#xff0c;将调试结果显示在计算机屏幕上&am…

第九章 进程和计划任务管理【☆】

一个进程可以创建多个子进程&#xff0c;子进程之间相互独立&#xff0c;速度较慢&#xff0c;但是互不影响。线程是共享资源&#xff0c;速度快&#xff0c;但一个线程崩掉其他线程同时崩掉。 一、查看进程信息 1. 查看静态的进程统计信息——ps命令 主要进程状态 R(runnin…

在控制台实现贪吃蛇

在控制台实现贪吃蛇 前备知识Win32APICOORD这个结构体的声明如下&#xff1a;GetStdHandle 函数GetConsoleCursorInfo 函数SetConsoleCursorInfo 函数 SetConsoleCursorPosition 函数getAsyncKeyState 函数 控制台窗口的大小以及字符打印介绍控制台中的坐标宽字符及本地化介绍s…

BBS前后端混合项目--03

展示 static/bootstrp # bootstrap.min.css /*!* Bootstrap v3.4.1 (https://getbootstrap.com/)* Copyright 2011-2019 Twitter, Inc.* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)*//*! normalize.css v3.0.3 | MIT License | github.com/n…

OSPF大型实验

OSPF大型实验 实验拓扑图 实验思路 1、R4为ISP&#xff0c;其上只配置IP地址&#xff1b;R4与其他所直连设备间均使用公有IP&#xff1b; 2、R3-R5、R6、R7为MGRE环境&#xff0c;R3为中心站点&#xff1b; 3、整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;除了R12有两…