图论

Posted on   jacyoier  阅读(105)  评论(0编辑  收藏  举报

图的概念:

 

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,到这里,我们已经把克鲁斯卡算法过了一遍

 

相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效

随笔 - 15, 文章 - 2, 评论 - 0, 阅读 - 967

Copyright © 2025 jacyoier
Powered by .NET 9.0 on Kubernetes

点击右上角即可分享
微信分享提示