【算法笔记】图论基础(一):建图、存图、树和图的遍历、拓扑排序、最小生成树
目录
- 何为图论
- 图的概念
- 图的一些基本概念
- 有向图和无向图
- 带权图
- 连通图和非连通图
- 对于无向图
- 对于有向图
- 度
- 对于无向图
- 对于有向图
- 一些结论
- 环
- 自环、重边、简单图、完全图
- 自环
- 重边
- 简单图
- 稀疏图和稠密图
- 子图、生成子图
- 同构
- 图的存储
- 直接存边
- 邻接矩阵存边
- 邻接表存边
- 链式前向星存边
- 图的遍历
- 树和图的遍历(图示)
- 树
- 图
- 树和图的深度优先遍历(dfs)
- 树和图的广度优先遍历(bfs)
- 拓扑排序
- 什么是拓扑排序?
- 结论
- 例题
- 一个技巧
- 最小生成树
- Kruskal求最小生成树
- 算法过程
- 图示
- 例题
- Prim求最小生成树
- 算法过程
- 图示
- 例题
- 最大生成树
何为图论
图的概念
图(Graph) 是一种数据结构,是由 节点(Node) 和 边(Edge) 组成的集合。
由节点所构成的集合就叫点集,由边所构成的集合就叫边集,点集和边集放在一起就形成了一张图。
注意:一张图的边集可以为空,但点集一定不能为空。
下面就是一个图,可以看到这个图有5个顶点,分别编号为{0,1,2,3,4}。同时这个图有5条边,例如,在顶点1和顶点3之间存在着一条边。

在图这个数据结构上面的所有问题和算法,统称为图论。
图的一些基本概念
有向图和无向图
图的边分为有向边和无向边,只有有向边的图就是有向图,只有无向边的图就是无向图。
上面的图片就是一个无向图,而如果把图改成这样,就是有向图。
如果对于边a -> b,a能到b、b不能到a,那这就是一条a到b的有向边。
如果对于边a – b,a能到b、b也能到a,那这就是一条a到b的无向边。
可以发现一个结论:无向边就是特殊的有向边、无向图就是特殊的有向图
解释:对于两个点a, b,你想在a、b之间连一条无向边,只需从a向b连一条有向边,再从b到a连一条有向边即可,就像下面这样:

所以我们研究的所有问题,只需考虑有向图即可,无向图只需在建图的时候建两条相反的有向边。
带权图
对于一个图,如果每一条边都被赋予一个数作为该边的权,这个图就是带权图。
边权其实也就是边长
比如下图

边权为正数的边就是正权边,比如上图中的(0, 1)、(2, 4)
边权为负数的边就是负权边,比如上图中的(1, 2)、(1, 3)
- 对于非带权图,表示的通常是节点和节点之间的连通关系
- 对于带权图,表示的同通常是节点和节点的距离
连通图和非连通图
对于无向图
对于无向图中的两个点u,v,如果存在一条从u到v的路径,则称为u和v连通。
连通图
非连通图
如果无向图的任意两个顶点均连通,这个图就是连通图,图的这一性质被称为连通性。
对于有向图
对于有向图中的两个点u,v,如果存在一条从u到v的路径,则称为u可达v。
若一张有向图的节点两两互相可达,则称这张图是强连通的
若一张有向图的边替换为无向边后可以得到一张连通图,则称原来这张有向图是弱连通的
弱连通图
强连通图
度
顶点连接的边数叫做这个顶点的度
对于无向图
- 无向图中一个点的度数就是这个点的临边数。
比如下面的无向图,d(0) = 2, d(1) = 3, d(4) = 1…

对于有向图
有向图的度数分为两种:入度和出度
对于无向图中的一个点,
- 它的入度等于一这个点为终点的边(即直接指向这个点的边)的数量
- 它的出度等于以这个点为起点的边(即从这个点指向别的点的边)的数量
比如下面的有向图,din(0) = 0,dout(0) = 2, din(1) = 1, dout(1) = 2, din(4) = 1, dout(4) = 0…

一些结论
1.在任意无向图中,所有节点的度数和等于总边数*2。
2. 在任意无向图中,度为奇数的顶点的个数为偶数。
3. 在任意有向图中,所有节点入度的和等于所有节点出度的和。
环
图中起点和终点重合的非空路径叫做环
比如下面的图中,0 -> 1 -> 2 -> 0这条路径就是一个环

没有环的图叫无环图,没有环的有向图叫有向无环图,没有环的连通图称为树。
自环、重边、简单图、完全图
自环
对于图中的一条边(u, v),如果u == v,那么这条边就被称做自环。
比如下图中的(2, 2)就是一个自环。

重边
如果图中有两条完全相同的边(起点、终点、方向都相同),那么他们就被称为一组重边。
比如下图中,(2, 4)和(2, 4)就是一组重边,但(0, 1) 和(1, 0)不是。

简单图
没有重边和自环的图被称作简单图。
任意两个节点都相邻的无向简单图被称为完全图。
若有向图满足任意不同两点间都有两条方向不同的边,则称为有向完全图。
稀疏图和稠密图
- 若一张图的边数远小于其点数的平方,那么它是一张 稀疏图 。
- 若一张图的边数接近其点数的平方,那么它是一张 稠密图 。
稀疏图和稠密图一般来讨论时间复杂度为 O ( V 2 ) O(V^2) O(V2)的算法和 O ( E ) O(E) O(E)的算法的效率差异,在稠密图上二者效率相当,在稀疏图上 O ( E ) O(E) O(E)的算法更优。
子图、生成子图
如果图a的节点集和边集分别是图b的节点集的子集和边集的子集,那么就称图a为图b的子图。
含有图G的所有顶点的子图称为图G的生成子图
如果一个生成子图是树(无环连通图),那么就称这个生成子图为生成树
比如下面这张图,就是上面那个出现很多次的无向图的一个生成树

同构
定义(图的同构)
给定两个图 G G G 和 H H H。若存在一个函数 f : V ( G ) → V ( H ) f: V(G) \to V(H) f:V(G)→V(H)满足对于任意顶点 u , v u, v u,v, ( u , v ) ∈ E ( G ) (u, v) \in E(G) (u,v)∈E(G) 当且仅当 ( f ( u ) , f ( v ) ) ∈ E ( H ) (f(u), f(v)) \in E(H) (f(u),f(v))∈E(H), 则称 f f f 为从 G G G 到 H H H 的一个同构,并且称图 G G G 与 H H H 是同构的,记作 G ≃ H G \simeq H G≃H
通俗的来讲,两个图同构就是结构相同,比如下面两个图就是同构的

图的存储
算法竞赛中,你想做图论的题,肯定得先学会怎么存图,图的存储主要分为以下几种方式:
直接存边
最简单粗暴的一种,用一个结构体数组直接存边
struct Edge{int a, b, w; // 表示一条 a->b 的边,边权为w
}edges[N];for(int i = 0; i < m; i++){ // 输入m条边int a, b, w;cin >> a >> b >> w;edges[i] = {a, b, w};
}
需要遍历所有边、做给边排序(比如Kruskal算法)等操作的时候常用这种存图方法。
邻接矩阵存边
邻接矩阵就是用一个二维数组来存边,数组的下标就是边的端点,数组的值就是边的权值。
对于不带权的图, g [ a ] [ b ] = 1 g[a][b] = 1 g[a][b]=1表示存在从a到b的边, g [ a ] [ b ] = 0 g[a][b] = 0 g[a][b]=0表示不存在,这时的邻接矩阵也叫可达性矩阵。
int g[N][N];while(m--){ // 输入m条边int a, b;cin >> a >> b;g[a][b] = 1;
}
对于带权图, g [ a ] [ b ] = w g[a][b] = w g[a][b]=w表示存在从a到b、权值为w的边, g [ a ] [ b ] = 0 g[a][b] = 0 g[a][b]=0表示不存在边
int g[N][N];while(m--){ // 输入m条边int a, b, w;cin >> a >> b >> w;g[a][b] = w;
}
邻接矩阵常用于稠密图,并且邻接矩阵只能用于没有重边或重边可以忽略的情况(比如求最短路时,如果有重边就只要最短的一条就可以了,其他的都可以忽略)。
邻接表存边
使用一个支持动态增加元素的数据结构构成的数组,如vector来存边。g[a]记录a的所有出边的信息(如终点、边权等)。
对于不带权的图,g[a]存的是所有以a为起点的边的终点。
vector<int> g[N];for(int i = 0; i < m; i++){int a, b;cin >> a >> b;g[a].push_back(b);
}for(int v : g[u]){ // 遍历u的所有出边,v就是出边的终点}
对于带权图,g[a]存的是所有以a为起点的边的终点和边权。
typedef pair<int, int> PII;
vector<PII> g[N];for(int i = 0; i < m; i++){int a, b, w;cin >> a >> b >> w;g[a].push_back({b, w});
}for(auto i : g[u]){ // 遍历u的所有出边int v = i.first, w = i.second; // v:出边的终点,w:边权
}
这是最常用的一种存边方法,存各种图都很适合。
链式前向星存边
链式前向星存图其实就是用单链表实现的的邻接表。
想用的话先去学链表,在这不做过多介绍。
int h[N], e[N], ne[N], idx;void add(int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx++; // add(a, b) : 加一条 a->b的边
}memset(h, -1, sizeof h); // 表头初始化成-1
idx = 0;for(int i = h[t]; i != -1; i = ne[i]){ // 遍历t的出边int j = e[i]; // j是出边的终点
}
int h[N], e[N], ne[N], w[N], idx;void add(int a, int b, int c){e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; // add(a, b, c) : 加一条 a->b 边权为c的边
}memset(h, -1, sizeof h); // 表头初始化成-1
idx = 0;for(int i = h[t]; i != -1; i = ne[i]){ // 遍历t的出边int j = e[i]; // j是出边的终点
}
存各种图都很适合,但不能快速查询一条边是否存在,也不能方便地对一个点的出边进行排序。
优点是边是带编号的,有时会非常有用,而且如果 cnt 的初始值为奇数,存双向边时 i ^ 1即是 i 的反边
图的遍历
树和图的遍历(图示)
树

dfs遍历顺序:1, 2, 5, 9, (5), 10, (5), (2), (1), 3, 6, 11,(6), (3), 7, (3), (1), 4, 8
bfs遍历顺序:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
图

dfs遍历顺序:1, 2, 5, 7, 6,(7),(5),(2),(1), 3, (1), 4
bfs遍历顺序:1, 2, 3, 4, 5, 6, 7
树和图的深度优先遍历(dfs)
和传统树的遍历一样,图的遍历也是一条路走到黑、不撞南墙不回头
给他一个遍历的起点,他会先走该点的第一条出边,然后再遍历终点的第一条出边…直到遍历到某个点没有出边了,再回溯去遍历第二条出边。
bool st[N];void dfs(int u){st[u] = true;...;for(int v : g[u]){if(st[v]) continue;dfs(v);...;}...;
}dfs(1);
树和图的广度优先遍历(bfs)
给他一个遍历的起点,他会把这个点所有的出边都走完,然后再去走所有第一步到的终点的所有出边…直到队首的点没有出边可走(队列为空,所有点就都遍历完了)
bool st[N];void bfs(int start){queue<int> q;q.push(start);st[start] = true;while(!q.empty()){int u = q.front();q.pop();...;for(int v : g[u]){if(st[v]) continue;q.push(v);st[v] = true;...;}}
}bfs(1);
拓扑排序
什么是拓扑排序?
一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。
一直进行上面的处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。
结论
一个图可以拓扑排序 ⇔ \Leftrightarrow ⇔这个图是有向无环图(DAG)
例题
848. 有向图的拓扑序列

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#define endl '\n'
using namespace std;
const int N = 1e5 + 10;
int n, m;
vector<int> g[N]; // 邻接表存边
int d[N]; // 存每个点的入度
int res[N]; // 存拓扑序列(答案)bool topsort(){int cnt = 0; // 记录当前删掉的节点数,也就是当前拓扑序列中的节点数queue<int> q;for(int i = 1; i <= n; i++){if(!d[i]) q.push(i); // 先把入度为0的点入队}while(!q.empty()){int t = q.front();q.pop();res[cnt++] = t; // 记录答案for(int i : g[t]){ // 遍历i的每条出边,给每个临点的入度-1(拿掉一条边)if(--d[i] == 0) q.push(i); // 将新的入度为0的点入队}}return cnt == n; // 如果每个点都能被删掉,就说明是有向无环图
}int main(){ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cin >> n >> m;while(m--){int a, b;cin >> a >> b;g[a].push_back(b); // 边 a->bd[b]++; // b入度+1}if(!topsort()) cout << -1 << endl;else{for(int i = 0; i < n; i++) cout << res[i] << ' ';}cout << endl;return 0;
}
一个技巧
一个有向无环图的拓扑序列是有很多种的,如果按上面的写法,就是先把所有入度为0的节点删除,再去删除其后继结点,但如果题目中要求优先输出编号大的或编号小的,把 q u e u e queue queue换成 p r i o r i t y _ q u e u e priority\_queue priority_queue就好了,维护一个大根堆(编号大的先输出)/小根堆(编号小的先输出),其他代码都一样。
例如:确定比赛名次
最小生成树
首先,如果一个生成子图是树(无环连通图),那么就称这个生成子图为生成树
一个无向图有很多的生成树,规定边权和最小的一个叫做最小生成树。
求最小生成树有两种最简单常用的算法,Kruskal算法和Prim算法,两种算法都是基于贪心的思想。
Kruskal求最小生成树
Kruskal算法非常好理解,简单粗暴,他是一个向图中加边的过程。
算法过程
- 将所有的边都拿掉,只留下所有的点,并将边按边权从小到大排序。
- 从小到大遍历每条边,看加上这条边会不会形成环,如果这个边与之前选择的所有边不会组成环,就选择这条边,将边权加到答案,反之,舍去。
- 遍历完所有边后,筛选出来的边和所有的顶点构成此图的最小生成树。
判断是否会产生回路的方法为:使用并查集。
- 在初始状态下给各个顶点在不同的集合中。
- 遍历过程的每条边,判断这两个顶点的是否在一个集合中
- 如果边上的这两个顶点在一个集合中,说明两个顶点已经连通,再加边就会成环,这条边不要。如果不在一个集合中,则要这条边。
图示

例题
Kruskal算法求最小生成树

#include <iostream>
#include <algorithm>
#include <cstring>
#define endl \n'
using namespace std;
const int N = 2e5 + 10;
int n, m;
int fa[N];struct Edge{int a, b, w; // 结构体数组存边
} edges[N];bool cmp(Edge a,Edge b){return a.w < b.w; // 按边权从小到大排序
}int find(int x){if(fa[x] != x)fa[x] = find(fa[x]);return fa[x];
}int kruskal(){sort(edges, edges + m, cmp); // 按边权从小到大排序for(int i = 1; i <= n; i++) fa[i] = i; // 并查集初始化int res = 0, cnt = 0; // res记录当前加到最小生成树中的边的权值的总和, cnt记录当前加了多少条边for(int i = 0; i < m; i++){int a = edges[i].a, b = edges[i].b, w = edges[i].w;a = find(a), b = find(b); // 并查集找祖先if(a != b){ // 如果a和b不在一个集合里,加边就不会成环fa[a] = b; // 加这条边res += w; // 将边权累加cnt++; // 又加进去一条边}}if(cnt < n - 1)return -1; // 加不到n - 1条边说明图不连通return res;
}int main(){ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cin >> n >> m;for (int i = 0; i < m; i++){int a, b, w;cin >> a >> b >> w;edges[i] = {a, b, w}; // m条边}int t = kruskal();if(t == -1) cout << "impossible" << endl;else cout << t << endl;return 0;
}
Prim求最小生成树
Prim算法是向图中加点的过程,他是维护了两个集合:{已经加到最小生成树中的点和边}(连通部分) 和 {还未加到最小生成树中的点和边},每次将离连通部分的最近的点和点对应的边加入连通部分,连通部分逐渐扩大,最后将整个图连通起来,并且边长之和最小,最后的连通部分就是该图的最小生成树。
算法过程
- 一开始,任取一个起点加入连通部分(第一次迭代)
- 接下来每次迭代,遍历所有不在连通部分的点,找到离连通部分最近的点,将其加入连通部分
- 用该点到其他点的距离更新其他点到连通部分的距离
- 继续下一次迭代,直到所有点都加到最小生成树中(由于一次加一个点,所以n个点只需迭代n 次)
- 最后迭代n次后的连通部分就是该图的最小生成树。
图示

例题
Prim算法求最小生成树

#include <iostream>
#include <algorithm>
#include <cstring>
#define endl '\n'
using namespace std;
const int N = 505;
int n, m;
int g[N][N]; // 邻接矩阵存图
int dist[N]; // dist[i]表示当前i距离连通部分的距离
bool st[N]; // st标记当前点是否已经加入连通部分,st[i] = true说明已经加入了,st[i] = false说明还没加入int prim(){memset(dist, 0x3f, sizeof dist); // 一开始,连通部分没有点,所有点到连通部分的距离为正无穷int res = 0; // res记录当前加到最小生成树中的边的权值的总和for(int i = 0; i < n; i++){ // 迭代n次int t = -1; // t来记录未加入连通部分的点中距离连通部分最近的点for(int j = 1; j <= n; j++){ // 遍历所有不在连通部分的点, 找到离连通部分最近的点if(!st[j] && (t == -1 || dist[t] > dist[j])) // t == -1 : 防止越界; dist[t] > dist[j]:点j比点t距离连通部分近t = j; // 把t换成j}if(i && dist[t] == 0x3f3f3f3f) // 如果连通部分有点,离连通部分最近的点dist还是正无穷,说明图不连通return 0x3f3f3f3f;if(i)res += dist[t]; // 从第二个加入连通部分的点开始,把其距离连通部分的距离加到答案(也就是将其对应的边加入连通部分)st[t] = 1; // 标记一下这个点加进去了for(int j = 1; j <= n; j++)dist[j] = min(dist[j], g[t][j]); // 遍历所有点,用t到其他点的距离更新其他点到连通部分的距离}return res;
}int main(){ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);cin >> n >> m;memset(g, 0x3f, sizeof g); // 由于要取min,所以初始化成正无穷while(m--){int a, b, w;cin >> a >> b >> w;g[a][b] = g[b][a] = min(g[a][b], w); // 求最小生成树,如果有重边就只要最短边}int t = prim();if(t == 0x3f3f3f3f) cout << "impossible" << endl;else cout << t << endl;return 0;
}
最大生成树
在求最小生成树的基础上,Kruskal就是把正序排序换成逆序排序,Prim就是把大于小于号调换一下,灵活一点。
相关文章:
【算法笔记】图论基础(一):建图、存图、树和图的遍历、拓扑排序、最小生成树
目录 何为图论图的概念 图的一些基本概念有向图和无向图带权图连通图和非连通图对于无向图对于有向图 度对于无向图对于有向图一些结论 环自环、重边、简单图、完全图自环重边简单图 稀疏图和稠密图子图、生成子图同构 图的存储直接存边邻接矩阵存边邻接表存边链式前向星存边 图…...
SpringMVC 请求与响应处理详解
引言 在 Java Web 开发中,SpringMVC 作为 Spring 框架的重要模块,提供了强大的请求和响应处理机制。本文将深入探讨 SpringMVC 中请求和响应的处理方式,结合实际案例,帮助开发者更好地理解和应用这些功能。 一、SpringMVC 请求处…...
【python】requests 爬虫高效获取游戏皮肤图
1. 引言 在当今的数字时代,游戏已经成为许多人生活中不可或缺的一部分。而游戏中的皮肤,作为玩家个性化表达的重要方式,更是受到了广泛的关注和喜爱。然而,对于许多玩家来说,获取游戏皮肤往往需要花费大量的时间和精力…...
(UI自动化测试web端)第二篇:元素定位的方法_css定位之ID选择器
看代码里的【find_element_by_css_selector( )】( )里的表达式怎么写? 文章介绍了第一种写法id选择器,其实XPath元素定位要比CSS好用,原因是CSS无法使用下标(工作当中也是常用的xpath),但CSS定位速度比XPat…...
23种设计模式-代理(Proxy)设计模式
代理设计模式 🚩什么是代理设计模式?🚩代理设计模式的特点🚩代理设计模式的结构🚩代理设计模式的优缺点🚩代理设计模式的Java实现🚩代码总结🚩总结 🚩什么是代理设计模式…...
【react18】react项目使用mock模拟后台接口
前后端分离项目,后端还没有接口的时候,前端可以使用mockjs的技术实行假数据的模拟。这里使用的是mock的库msw实现这个业务. MSW msw是mock的工具,官网地址是在这里 使用步骤 1.安装msw npm install mswlatest --save-dev2.新建存放mock接…...
Excel新增的函数
常用函数 XLOOKUP 1、普通查找 2、屏蔽错误值 3、横向查找 4、通配符查找 ?:代表任意单个字符 *:代表任意多个字符 5、反向查找 6、多条件查找 7、查找多列数据 8、查找最后一个 IFS MINIFS MAXIFS 文本函数 TEXTSPLIT、TEXTJOIN、CONCAT、 TEXTBEFORE、TEXTAFT…...
Windows下VSCode的安装
前言 VSCode的安装看起来平平无奇,但也不是轻轻松松的。笔者将最新的Windows下安装VSCode,以及运行最简单的C程序的过程记录下来,供后续的自己和大家参考。 一、官网下载安装包 Visual Studio Code - Code Editing. Redefined 二、安装 直接…...
django入门教程之templates和static资源【五】
使用app01子应用举例说明模板的使用。templates官方文档。 templates完整流程认知 第一步,在settings.py中注册app01子应用。 第二步,在app01目录下,新建templates和static目录,用于存放模板文件和资源文件。目录结构如下&#…...
Vue 中directive的钩子函数(bind、inserted 等)的作用及使用场景
大白话Vue 中directive的钩子函数(bind、inserted 等)的作用及使用场景。 在 Vue 里,指令(directive)是个超实用的东西,它能让你在不改动组件逻辑的情况下,给 HTML 元素添加一些特殊的行为。Vu…...
【区块链安全 | 第一篇】密码学原理
文章目录 1.哈希函数1.1 哈希函数的性质1.2 常见哈希算法1.3 Merkle Tree(默克尔树)1.4 HMAC(哈希消息认证码) 2. 公钥密码学2.1 对称加密 vs 非对称加密2.2 RSA 算法2.3 ECC(椭圆曲线密码学)2.4 Diffie-He…...
Linux安装MySQL数据库并使用C语言进行数据库开发
目录 一、前言 二、安装VMware运行Ubuntu 1.安装VMware 2.使用VMware打开Ubuntu 三、配置VMware使用网卡 1.添加NAT网卡 四、Linux下安装MySQL数据库 五、安装MySQL开发库 六、演示代码 sql_connect.c sql_connect.h main.c中数据库相关代码 结尾 一、前言 由于最…...
2024年MathorCup数学建模A题移动通信网络中PCI规划问题解题全过程文档加程序
2024年第十四届MathorCup高校数学建模挑战赛 A题 移动通信网络中PCI规划问题 原题再现: 物理小区识别码(PCI)规划是移动通信网络中下行链路层上,对各覆盖小区编号进行合理配置,以避免PCI冲突、PCI混淆以及PCI模3干扰等现象。PCI规划对于减少…...
伯努利分布和二项分布学习笔记
目录 1. 伯努利分布1.1定义1.2概率质量函数1.3数学期望与方差1.4应用示例 2. 二项分布2.1定义2.1概率质量函数2.2数学期望与方差2.3性质与图形 3. 伯努利分布与二项分布的关系4. 总结 1. 伯努利分布 伯努利分布(Bernoulli Distribution),又称…...
Redis实战常用二、缓存的使用
一、什么是缓存 在实际开发中,系统需要"避震器",防止过高的数据访问猛冲系统,导致其操作线程无法及时处理信息而瘫痪. 这在实际开发中对企业讲,对产品口碑,用户评价都是致命的。所以企业非常重视缓存技术; 缓存(Cache):就是数据交换的缓冲区&…...
G口服务器和普通服务器之间的区别
今天小编主要来为大家介绍一下G口服务器和普通服务器之间的区别! 首先,从硬件配置上看,普通服务器通常都会配备中央处理器、内存和硬盘等基本的硬件配置,能够适用于各种应用程序和服务;G口服务器除了基础的硬件配置还增…...
通过国内源在Ubuntu20.0.4安装repo
国内三大免费源: 清华大学:清华大学开源软件镜像站 | Tsinghua Open Source Mirror中国科技大学:USTC Open Source Software Mirror阿里云:阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 repo只在清华源网站里搜到:…...
多维动态规划 力扣hot100热门面试算法题 面试基础 核心思路 背题
多维动态规划 不同路径 https://leetcode.cn/problems/unique-paths/ 核心思路 比较简单 f[i][j] f[i - 1][j] f[i][j - 1] ; 示例代码 class Solution {public int uniquePaths(int n, int m) {int[][] f new int[n][m];for (int i 0; i < n; i)f[i][0] 1;for…...
《Java到Go的平滑转型指南》
文章目录 **文章摘要****核心主题****关键内容提炼****决策者行动清单****核心结论** **第一章:转型决策:为什么要从Java转向Go?****1.1 性能对比:GC机制与并发模型差异****GC机制对比****并发模型基准测试** **1.2 开发效率&…...
【软件测试】:软件测试实战
1. ⾃动化实施步骤 1.1 编写web测试⽤例 1.2 ⾃动化测试脚本开发 common public class AutotestUtils {public static EdgeDriver driver;// 创建驱动对象public static EdgeDriver createDriver(){// 驱动对象已经创建好了 / 没有创建if( driver null){driver new EdgeDr…...
SpringMVC 请求处理
SpringMVC 请求处理深度解析:从原理到企业级应用实践 一、架构演进与核心组件协同 1.1 从传统Servlet到前端控制器模式 SpringMVC采用前端控制器架构模式,通过DispatcherServlet统一处理请求,相比传统Servlet的分散处理方式,实…...
unittest自动化测试实战
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 为什么要学习unittest 按照测试阶段来划分,可以将测试分为单元测试、集成测试、系统测试和验收测试。单元测试是指对软件中的最小可测试单元在与程…...
leetcode3.无重复字符的最长字串
采用滑动窗口方法 class Solution { public:int lengthOfLongestSubstring(string s) {int ns.size();if(n0)return 0;int result0;unordered_set<char> set;set.insert(s[0]);for(int i0,j0;i<n;i){while(j1<n&&set.find(s[j1])set.end()){set.insert(s[…...
Android Compose 框架派生状态(derivedStateOf、rememberCoroutineScope)深入剖析(十五)
一、引言 在 Android 开发领域,高效的状态管理对于构建响应式、高性能的应用程序至关重要,在 Jetpack Compose 中,derivedStateOf 和 rememberCoroutineScope 这两个与派生状态相关的特性在状态管理方面发挥着关键作用。派生状态允许我们根据…...
3.25-2request库
request库 一、介绍request库 (1)requests是用python语言编写的简单易用的http库,用来做接口测试的库; (2)接口测试自动化库有哪些? requests、urllib 、urllib2、urllib3、 httplib 等&…...
《破解老龄化的智能密钥:机器人四维战略与未来养老生态》
一、引言:老龄化社会与智能机器人的必然性 全球老龄化趋势与老年人核心需求(健康管理、生活辅助、心理陪伴、安全保障) 全球正面临着严峻的老龄化挑战。根据联合国发布的数据,全球60岁及以上人口数量在过去几十年中持续增长&…...
Docker-Volume数据卷详讲
Docker数据卷-Volume 一:Volume是什么,用来做什么的 当删除docker容器时,容器内部的文件就会跟随容器所销毁,在生产环境中我们需要将数据持久化保存,就催生了将容器内部的数据保存在宿主机的需求,volume …...
SpringMVC 配置
一、MVC 模式简介 在软件开发的广袤天地中,MVC 模式宛如一座明亮的灯塔,指引着开发者构建高效、可维护的应用程序。Spring MVC 作为基于 Spring 框架的重要 web 开发模块,更是将 MVC 模式的优势发挥得淋漓尽致,堪称 Servlet 的强…...
Python 3.8 Requests 爬虫教程(2025最新版)
遵守网站的爬虫规则、避免爬取敏感信息、保护个人隐私! 一、环境配置与基础验证 # 验证 Python 版本(需 ≥3.8) import sys print(sys.version) # 应输出类似 3.8.12 的信息# 安装 requests 库(若未安装) # 命令行执…...
蓝桥杯备考之 最长上升子序列问题(挖地雷)
这道题其实就是正常的最长上升子序列问题,但是我们还要把最优方案输出出来,我们可以用个pre数组来维护就行了,每当我们更新以i为结尾的最长子序列,如果i是接在1到i-1某个点后面的话就把前面的点存到pre里面 最后我们把pre倒着打印…...





