图的概念:
1、图的概念
G=(V,E)
图G由节点集合V=V(G)和边集合E=E(G)组成,其中V为非空有限集合。
集合V中的节点(node)用红色标出,通过集合E中黑色的边(edge)连接。
G的边:E中的每个顶点对(u,v)称为G的边
边的端点:用e表示集合E中的一个顶点对e=(u, v),称u,v为边e的端点
邻接顶点:称u和v是邻接的顶点
关联:一条边的端点称为与这条边关联
邻接的边:若两条不同的边与一个公共的端点关联,称这两条边是邻接的
多重边:若联结两个顶点有不止一条边,这些边称为多重边
环:顶点重合为一点的边称为环
简单图 :没有环也没有多重边的图称为简单图
空图:没有边的图称为空图
完全图:完全图是一个简单的无向图,其中每对不同的顶点之间都有一条边相连
子图:所有顶点和边都属于图G的图称为G的子图
2、连通图
连通图:图中任意两点之间均至少有一条通路,否则称为不连通图
连通图:在无向图中, 若从顶点v1到顶点v2有路径, 则称顶点v1与v2是连通的。如果图中任意一对顶点都是连通的,则称此图是连通图。
强连通和弱连通的概念只在有向图中存在
强连通图:在有向图中, 若对于每一对顶点v1和v2, 都存在一条从v1到v2和从v2到v1的路径,则称此图是强连通图。
弱连通图:将有向图的所有的有向边替换为无向边,所得到的图称为原图的基图。如果一个有向图的基图是连通图,则有向图是弱连通图。
1)连通图和完全图的区分(无向图)
总结性话语: 完全图一定是连通图,但连通图不一定是完全图。
定义
连通图:图中任意两个顶点都有 路径 存在
完全图:图中任意两个顶点都有 边 存在
解释
路径,可以是借道到达目标顶点
边,一定是两个顶点相连
2)强连通和有向完全图的区分(有向图)
总结性话语: 有向完全图一定是强连通图,但强连通图不一定是有向完全图
定义:
强连通:图中任何两个顶点都有 路径 存在
有向完全图:图中任意两个顶点都有 方向相反的两条边 存在
解释
路径,同上
方向相反的两条边,如 A --> B,B --> A,这是直接与两个顶点发生关系的。
无向图
相邻:两个顶点之间有边。
路径:相邻顶点序列称为路径。
圈:起点和终点重合的路径称为圈。
连通图:任意两点之间都有路径连接的图叫做连通图。
度:顶点连接的边数叫做这个顶点的度。
树:没有圈的连通图叫做树。
树的重要性质:一棵树的边数恰好是顶点数-1;反之,边数等于顶点数-1的连通图就是一颗树。
有向图
DAG:没有圈的有向图叫做DAG。
生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n - 1条边
最小生成树:一个连通图的生成树可能有多个。边的权值之和最小的生成树是最小生成树
完全图:
无向完全图:在n个顶点的无向图中,若有n(n-1)/2条边,即任意两个顶点之间有且仅有一条边,则称此图为无向完全图
有向完全图:在n个顶点的有向图中,若有n(n-1)条边,即任意两个顶点之间有且仅有方向相反的边,则称此图为有向完全图
最小生成树算法:
参考Dijkstra迪杰斯特拉算法 C++实现_dijkstra算法 两个方向权重不一样_Cy_coding的博客-CSDN博客
1、迪杰斯特拉
定义:从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
其中,ABCDE就是我们需要遍历的节点,连接节点的弦上的数字表示了两节点之间的"距离",或称之为权重,消耗。需要注意的几点是 :
迪杰斯特拉算法适用的有权图中,节点之间的权重不要求是双向的,即 A-> B的权重和 B->A 的权重不要求相同。换而言之,有权图中节点之间的连接可以是单向的,或者在两个方向权重有所不同的。
迪杰斯特拉算法适用的有权图中,节点间连接的权重值不能是负值。
了解了迪杰斯特拉算法的一些注意点之后,我们下面来重点解释算法的实现。之前我们已经提到了,迪杰斯特拉算法多用于解决最短路径问题,对应上述有权图就是计算出所有节点到初始节点的最短距离。在下述例子中,我们默认使用A作为初始节点,目标就是找出所有节点到A的最短距离以及所经过的路径。
首先我们需要两个列表,一个visited列表用于存放已经遍历过其所有邻节点的节点,一个unvisited列表用于存放还未遍历过其所有邻节点的节点。我们会不断地进行迭代运算,直到unvisited列表为空,即所有的节点都已经访问过(遍历过其所有邻节点)。
显然初始状态,visited列表为空[],unvisited列表中包含所有的节点[A,B,C,D,E]。
然后我们需要一个列表用于记录所有节点到A节点,起始节点之间的最短距离,以及最短路径中该节点之前的节点。
第一步,初始化这个表格 :
显然A到A的距离为0,其余节点到A的距离为未知,初始化为正无穷。
那么每一次迭代,我们需要做的,就是在unvisited列表中,选择一个到A距离最短的节点,并遍历更新其所有unvisited列表中的邻节点。
例如第一次迭代中,unvisited未访问列表中A节点到A的最短距离最短,那么我们首先就访问A所有unvisited的节点 B 和 D。简单来说,如果通过A访问B比起之前的方式要距离更短,那么我们就更新B到初始点的距离为新的最短距离,并将B之前的节点更新为A。那么在这个例子中,通过A访问B的距离为6,通过A访问D的距离为1,显然距离都比正无穷要小,则更新列表如下 :
弗洛伊德
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int INF = INT_MAX; 4 int n,m,a[1001][1001],f[1001][1001]; 5 int main(){ 6 ios::sync_with_stdio(false); 7 cin>>n>>m; 8 for(int i=1;i<=n;i++){ 9 for(int j=1;j<=n;j++){ 10 if(i==j) f[i][j]=0; 11 else f[i][j]=INF; 12 } 13 } 14 int a,b,c; 15 for(int i=0;i<m;i++){ 16 cin>>a>>b>>c; 17 f[a][b]=c; 18 } 19 for(int k=1;k<=n;k++){ 20 for(int i=1;i<=n;i++){ 21 for(int j=1;j<=n;j++){ 22 if(f[i][j]>f[i][k]+f[k][j]){ 23 f[i][j]=f[i][k]+f[k][j]; 24 } 25 } 26 } 27 } 28 for(int i=1;i<=n;i++){ 29 for(int j=1;j<=n;j++){ 30 cout<<f[i][j]<<" "; 31 } 32 cout<<endl; 33 } 34 return 0; 35 }
图论学习笔记 - _逃离地球 - 博客园 (cnblogs.com)
最小生成树:
Prim算法
算法思路:
从已选顶点所关联的未选边中找出权重最小的边,并且生成树不存在环。
其中,已选顶点是构成最小生成树的结点,未选边是不属于生成树中的边。
(普里姆算法与求最短路径的迪杰斯塔拉算法思想很类似)
下面我们对下面这幅图求其最小生成树:
假设我们从顶点v1开始,所以我们可以发现(v1,v3)边的权重最小,所以第一个输出的边就是:v1—v3=1:
然后,我们要从v1和v3作为起点的边中寻找权重最小的边,首先了(v1,v3)已经访问过了,所以我们从其他边中寻找,发现(v3,v6)这条边最小,所以输出边就是:v3—-v6=4
然后,我们要从v1、v3、v6这三个点相关联的边中寻找一条权重最小的边,我们可以发现边(v6,v4)权重最小,所以输出边就是:v6—-v4=2.
然后,我们就从v1、v3、v6、v4这四个顶点相关联的边中寻找权重最小的边,发现边(v3,v2)的权重最小,所以输出边:v3—–v2=5
然后,我们就从v1、v3、v6、v4,v2这2五个顶点相关联的边中寻找权重最小的边,发现边(v2,v5)的权重最小,所以输出边:v2—–v5=3
最后,我们发现六个点都已经加入到集合U了,我们的最小生成树建立完成。
克鲁斯卡算法
算法思路:
(1)将边按权值从小到大的顺序添加到新图中,保证添加的过程中不会形成环
(2)重复上一步直到连接所有顶点,此时就生成了最小生成树。这是一种贪心策略。
将图中所有边按照权重的大小 从小到大一个一个按顺序组合成最小生成树,在组合过程新加入的边会导致生成树形成环,那这条边就舍弃,直到所有顶点都添加到生成树中为止。
这里同样我们给出一个和Prim算法讲解中同样的例子,模拟克鲁斯卡算法生成最小生成树的详细的过程:
首先完整的图如下图:
然后,我们需要从这些边中找出权重最小的那条边,可以发现边(v1,v3)这条边的权重是最小的,所以我们输出边:v1—-v3=1
然后,我们需要在剩余的边中,再次寻找一条权重最小的边,可以发现边(v4,v6)这条边的权重最小,所以输出边:v4—v6=2
然后,我们再次从剩余边中寻找权重最小的边,发现边(v2,v5)的权重最小,所以可以输出边:v2—-v5=3,
然后,我们使用同样的方式找出了权重最小的边:(v3,v6),所以我们输出边:v3—-v6=4
好了,现在我们还需要找出最后一条边就可以构造出一颗最小生成树,但是这个时候我们有三个选择:(v1,V4),(v2,v3),(v3,v4),这三条边的权重都是5,首先我们如果选(v1,v4)的话,得到的图如下:
我们发现,这肯定是不符合我们算法要求的,因为它出现了一个环,所以我们再使用第二个(v2,v3)试试,得到图形如下:
我们发现,这个图中没有环出现,而且把所有的顶点都加入到了这颗树上了,所以(v2,v3)就是我们所需要的边,所以最后一个输出的边就是:v2—-v3=5
OK,到这里,我们已经把克鲁斯卡算法过了一遍
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效