图论相关算法1
图论算法详解(C++版)

1.1、prim算法:无向图的生成树就是从图的边集中选择一些边,使得这些边构成一个连通无环图,也就是树。
如果给每一条边加一个权,所有生成树中权和最小的生成树称为最小生成树。
【Prim算法思想】任意时刻的中间结果都是一棵树,每次花费最小的代价,用一条边把不在树中的结点加进来。
【最小生成树算法实例】现有一张城市地图,图中的顶点为城市,无向边代表两个城市间的连通关系,边上的权代表公路造价。
在分析了这张图后发现,任一对城市都是连通的。
现在要求用公路把所有城市联系起来,如何设计可使得工程的总造价最少?【输入】第一行两个数v(v<=200),e,分别代表城市数和边数以下e行,每行为两个顶点和它们之间的边权w(w<1000)。
【输出】连通所有城市的公路最小造价。
【输入样例】6 101 2 101 5 191 6 212 3 52 4 62 6 113 4 64 5 184 6 145 6 33【输出样例】50 原图最小生成树#include<cstdio>#include<string>#include<cstring>#include<climits>using namespace std;int i,j,k,n,m,mi,t,s,a[1000][1000]; void prim(){int mi,p,f,k,d[1000];bool v[1000];memset(v,false,sizeof(v));f=1;for (i=2;i<=n;i++){d[i]=INT_MAX;}d[f]=0;s=0;for(i=1;i<=n;i++){mi=INT_MAX;for (j=1;j<=n;j++)if ((v[j]==false) && (d[j]<mi)){p=j;mi=d[j];}s+=mi;v[p]=true;for(j=1;j<=n;j++){if (a[p][j]<d[j]) d[j]=a[p][j];}}}int main(){memset(a,0,sizeof(a));scanf("%d%d",&n,&m);mi=INT_MAX;for (i=1;i<=n;i++){for (j=1;j<=n;j++){a[i][j]=INT_MAX;}}for (i=1;i<=m;i++){scanf("%d%d%d",&k,&j,&t);if ((t<a[k][j])||(t<a[j][k])){a[k][j]=t;a[j][k]=a[k][j];}}prim();printf("%d",s);return 0;}1.2、克鲁斯卡尔算法假设N=(V,{E})是连通网,将N中的边按权值从小到大的顺序排列;①、将n个顶点看成n个集合;②、按权值小到大的顺序选择边,所选边应满足两个顶点不在同一个顶点集合内,将该边放到生成树边的集合中。
图论中的生成树计数算法

图论中的生成树计数算法生成树是图论中重要的概念之一,它是指由给定图的节点组成的树形结构,其中包含了原图中的所有节点,但是边的数量最少。
生成树的计数问题是指在一个给定的图中,有多少种不同的生成树。
生成树计数算法是解决这个问题的关键步骤,本文将介绍一些常见的生成树计数算法及其应用。
1. Kirchhoff矩阵树定理Kirchhoff矩阵树定理是图论中经典的生成树计数方法之一。
该定理是由Kirchhoff在19世纪提出的,它建立了图的Laplacian矩阵与其生成树个数的关系。
Laplacian矩阵是一个$n\times n$的矩阵,其中$n$是图中的节点数。
对于一个连通图而言,Laplacian矩阵的任意一个$n-1$阶主子式,其绝对值等于该图中生成树的个数。
应用示例:假设我们有一个无向连通图,其中每个节点之间的边权均为1。
我们可以通过计算图的Laplacian矩阵的任意一个$n-1$阶主子式的绝对值来得到该图中的生成树个数。
2. Prufer编码Prufer编码是一种编码方法,可用于求解生成树计数问题。
它是基于树的叶子节点的度数的编码方式。
Prufer编码将一个树转换为一个长度为$n-2$的序列,其中$n$是树中的节点数。
通过给定的Prufer序列,可以构造出对应的生成树。
应用示例:假设我们有一个具有$n$个节点的有标号的无根树。
我们可以通过构造一个长度为$n-2$的Prufer序列,然后根据Prufer编码的规则构造出对应的生成树。
3. 生成函数方法生成函数方法是一种利用形式幂级数求解生成树计数问题的方法。
通过将图的生成树计数问题转化为生成函数的乘法运算,可以得到生成函数的一个闭形式表达式,从而求解生成树的个数。
应用示例:假设我们有一个具有$n$个节点的有根树,其中根节点的度数为$d$。
我们可以通过生成函数方法求解出该有根树中的生成树个数。
4. Matrix-Tree定理Matrix-Tree定理是对Kirchhoff矩阵树定理的一种扩展,适用于带权图中生成树计数的问题。
图论中的常用经典算法

图论中的常用经典算法第一节最小生成树算法一、生成树的概念若图是连通的无向图或强连通的有向图,则从其中任一个顶点出发调用一次bfs或dfs后便可以系统地访问图中所有顶点;若图是有根的有向图,则从根出发通过调用一次dfs或bfs亦可系统地访问所有顶点。
在这种情况下,图中所有顶点加上遍历过程中经过的边所构成的子图称为原图的生成树。
对于不连通的无向图和不是强连通的有向图,若有根或者从根外的任意顶点出发,调用一次bfs或dfs后不能系统地访问所有顶点,而只能得到以出发点为根的连通分支(或强连通分支)的生成树。
要访问其它顶点则还需要从没有访问过的顶点中找一个顶点作为起始点,再次调用bfs 或dfs,这样得到的是生成森林。
由此可以看出,一个图的生成树是不唯一的,不同的搜索方法可以得到不同的生成树,即使是同一种搜索方法,出发点不同亦可导致不同的生成树。
如下图:但不管如何,我们都可以证明:具有n个顶点的带权连通图,其对应的生成树有n-1条边。
二、求图的最小生成树算法严格来说,如果图G=(V,E)是一个连通的无向图,则把它的全部顶点V和一部分边E’构成一个子图G’,即G’=(V, E’),且边集E’能将图中所有顶点连通又不形成回路,则称子图G’是图G的一棵生成树。
对于加权连通图,生成树的权即为生成树中所有边上的权值总和,权值最小的生成树称为图的最小生成树。
求图的最小生成树具有很高的实际应用价值,比如下面的这个例题。
例1、城市公交网[问题描述]有一张城市地图,图中的顶点为城市,无向边代表两个城市间的连通关系,边上的权为在这两个城市之间修建高速公路的造价,研究后发现,这个地图有一个特点,即任一对城市都是连通的。
现在的问题是,要修建若干高速公路把所有城市联系起来,问如何设计可使得工程的总造价最少。
[输入]n(城市数,1<=n<=100)e(边数)以下e行,每行3个数i,j,w ij,表示在城市i,j之间修建高速公路的造价。
图论在交通网络优化中的应用

图论在交通网络优化中的应用交通网络的优化一直是一个重要的研究领域,通过合理的路线规划和流量管理,可以提高交通效率,减少拥堵和能源消耗。
图论作为数学的一个分支,广泛应用于交通网络优化中,帮助我们解决这些问题。
本文将探讨图论在交通网络优化中的应用,并介绍一些经典的图论算法。
一、交通网络模型与图论在研究交通网络优化之前,我们需要将交通网络抽象成数学模型。
交通网络通常可以用图的形式来表示,其中路口是节点,道路是边。
图论提供了一些基本的概念和方法来描述和分析交通网络。
1. 图的基本概念- 节点(vertex):在交通网络中,节点表示路口或交叉口。
每个节点可以有多个与之相连的边,表示与其他路口的连接。
- 边(edge):边表示路径,连接两个节点。
在交通网络中,边可以是双向的,也可以是单向的。
- 权重(weight):边上的权重表示从一个节点到另一个节点的代价或距离。
在交通网络中,权重可以表示道路的长度、通行能力或其他影响路线选择的因素。
2. 图的类型- 无向图(undirected graph):在无向图中,边没有方向,可以从一个节点到另一个节点,也可以反过来。
- 有向图(directed graph):在有向图中,边有方向,只能从一个节点指向另一个节点。
- 带权图(weighted graph):在带权图中,边上有权重值,可以表示路径的距离、时间或其他影响因素。
二、最短路径算法最短路径算法是图论中最基本且常用的问题之一,在交通网络优化中具有重要的应用。
最短路径算法旨在找到两个节点之间的最短路径,这对于寻找出行路线、减少交通拥堵、优化路径规划等都是至关重要的。
1. 迪杰斯特拉算法(Dijkstra's algorithm)迪杰斯特拉算法是一种解决单源最短路径问题的贪心算法。
通过逐步选择离源节点最近的节点,并更新到达其他节点的最短距离,最终找到源节点到其他所有节点的最短路径。
这个算法可以用于交通网络中,帮助人们找到最佳的出行路线。
图论中的最短路径算法

图论中的最短路径算法图论是数学的一个分支,研究图的性质和图之间的关系。
在图论中,最短路径算法是一类重要的算法,用于寻找图中两个顶点之间的最短路径。
本文将介绍图论中的几种常见的最短路径算法。
一、Dijkstra算法Dijkstra算法是最短路径算法中最常用的一种。
它基于贪心策略,通过逐步扩展路径来求解最短路径。
算法的基本思想是,从一个起始顶点开始,逐步扩展到其他顶点,每次选择当前路径中距离起始顶点最近的顶点进行扩展,直到扩展到目标顶点或者所有顶点都被扩展完毕。
Dijkstra算法的步骤如下:1. 初始化起始顶点的距离为0,其他顶点的距离为无穷大。
2. 选择距离起始顶点最近的顶点,将其加入已扩展顶点集合。
3. 更新与新加入顶点相邻的顶点的距离,如果新的距离比原来的距离小,则更新距离。
4. 重复步骤2和步骤3,直到扩展到目标顶点或者所有顶点都被扩展完毕。
5. 根据更新后的距离,可以得到最短路径。
二、Bellman-Ford算法Bellman-Ford算法是另一种常用的最短路径算法。
它可以处理带有负权边的图,而Dijkstra算法只适用于非负权边的图。
Bellman-Ford算法的基本思想是通过对所有边进行松弛操作,逐步减小起始顶点到其他顶点的估计距离,直到得到最短路径。
Bellman-Ford算法的步骤如下:1. 初始化起始顶点的距离为0,其他顶点的距离为无穷大。
2. 对所有边进行松弛操作,即如果存在一条边(u, v),使得从起始顶点到v的距离大于从起始顶点到u的距离加上边(u, v)的权值,则更新距离。
3. 重复步骤2,直到没有顶点的距离发生变化。
4. 根据更新后的距离,可以得到最短路径。
三、Floyd-Warshall算法Floyd-Warshall算法是一种多源最短路径算法,可以求解图中任意两个顶点之间的最短路径。
该算法通过动态规划的方式,逐步更新顶点之间的距离,直到得到最短路径。
Floyd-Warshall算法的步骤如下:1. 初始化顶点之间的距离矩阵,如果两个顶点之间存在边,则距离为边的权值,否则距离为无穷大。
图论的基础概念和算法

图论的基础概念和算法图论是数学的一个分支,研究的对象是图。
图是由一组互不相连的节点(顶点)和连接这些节点的边(边)组成的数学结构。
图论的基础概念包括顶点、边、路径、环、度数等。
本文将介绍图论的基础概念以及常用的图算法。
一、基础概念1. 图的定义和表示图由顶点集合和边集合组成。
顶点集合用V表示,边集合用E表示。
图可以用邻接矩阵或邻接表来表示。
邻接矩阵是一个二维数组,用来表示图中顶点之间的连接关系。
邻接表是一个链表数组,用来表示每个顶点相邻顶点的列表。
2. 顶点和边顶点是图的基本组成单位,用来表示图中的一个节点。
边是连接两个顶点的线段,用来表示两个顶点之间的关系。
3. 路径和环路径是由一系列相邻顶点连接而成的顶点序列。
路径的长度是指路径上经过的边的数目。
环是起点和终点相同的路径。
4. 度数顶点的度数是指与其相邻的边的数目。
入度是指指向该顶点的边的数目,出度是指由该顶点指向其他顶点的边的数目。
图中顶点的度数可以用来判断顶点的重要性。
二、常用算法1. 广度优先搜索(BFS)广度优先搜索是一种用来遍历和搜索图的算法。
从一个起始顶点开始,逐层扩展,先访问距离起始顶点最近的顶点,然后访问它们的相邻顶点,并逐渐向外扩展。
广度优先搜索可以用来计算两个顶点之间的最短路径。
2. 深度优先搜索(DFS)深度优先搜索是另一种常用的图遍历算法。
从一个起始顶点开始,沿着一条路径尽可能深入地访问图,直到不能再继续深入为止,然后回溯到上一个顶点,继续探索其他路径。
深度优先搜索可以用来计算连通分量、拓扑排序和寻找环等。
3. 最小生成树最小生成树是指图中通过连接所有顶点的子图,并且该子图的边权重之和最小。
常用的最小生成树算法包括Prim算法和Kruskal算法。
Prim算法从一个顶点开始,逐步扩展最小生成树的边,直到包含所有顶点为止。
Kruskal算法则是从边的权重最小的边开始,逐步增加边到最小生成树中,直到包含所有顶点为止。
4. 最短路径算法最短路径算法用来计算两个顶点之间的最短路径。
离散数学中常用的图论算法简介
离散数学中常用的图论算法简介图论是高等数学中的一个分支,主要涉及在图中寻找什么样的路径,以及什么样的点之间有什么样的关系。
在计算机科学中,图论的应用越来越广泛。
因为所有的计算机程序都是基于数据结构的,而图是一种最基本的数据结构之一。
离散数学中的图论算法大致可以分为两类:一类是针对稠密图的算法,另一类是针对稀疏图的算法。
稠密图指的是一种图,其中每对顶点都有一条边相连,而稀疏图则是指只有一部分顶点之间相连的图。
以下是一些常见的图论算法的简介。
1. Dijkstra算法Dijkstra算法是一种用于求图中最短路径的算法,也是最常用的图论算法之一。
Dijkstra算法的主要思想是通过贪心策略,从起点出发,逐步扩展最短路径的范围,直到找到终点。
Dijkstra算法可以用来解决单源最短路径问题。
如果图中有n个顶点,算法的时间复杂度为O(n²)。
2. Kruskal算法Kruskal算法是一种用于求最小生成树的算法。
最小生成树指的是,通过连接图中一些顶点形成一棵树,使得这些顶点之间的总权重最小。
Kruskal算法的主要思想是将图中的所有边按照权重进行排序,然后依次加入到生成树中,如果新加入的边会形成环,则不将其加入到生成树中。
如果图中有n个顶点,那么算法的时间复杂度为O(nlogn)。
3. Floyd算法Floyd算法用于求图中任意两个点之间的最短路径。
如果图中所有的权重都是正的,那么Floyd算法的时间复杂度为O(n的三次方),但是如果存在负权重,那么该算法不适用。
关于负权环的处理,可以通过Bellman-Ford算法进行解决。
4. Prim算法Prim算法是一种用于求最小生成树的算法。
与Kruskal算法不同的是,Prim算法是基于顶点集来实现,而不是基于边集。
Prim 算法首先找到一个起点,将其加入到生成树中,然后找到与其相连的边中权重最小的那一条,将其相连的顶点加入到生成树中,重复这一步骤直至所有顶点都被加入到生成树中。
图论算法(C版)
•
int circuit[maxn];
//用来记录找到的欧拉路的路径
•
int n,e,circuitpos,i,j,x,y,start;
•
void find_circuit(int i)
//这个点深度优先遍历过程寻找欧拉路
•
{
•
int j;
•
for (j = 1; j <= n; j++)
•
if (g[i][j] == 1)
•}
• 主程序如下:
• int main()
•{
• ……
• memset(visited,false,sizeof(visited));
• for (int i = 1; i <= n; i++)
//每一个点都作为起点尝试访问,因为不是从任何
•
//一点开始都能遍历整个图的,例如下面的两个图。
•
if (!visited[i])
• (b)无向图:图的边没有方向,可以双向。(b)就是一个无向图。
1
1
• 结点的度:无向图中与结点相连的边的数目,称为结点的度。 5
25
2
• 结点的入度:在有向图中,以这个结点为终点的有向边的数目。 4
• 结点的出度:在有向图中,以这个结点为起点的有向边的数目。
3
43
• 权值:边的“费用”,可以形象地理解为边的长度。
•
}
•
…………
•
return 0;
•}
• 建立邻接矩阵时,有两个小技巧:
•
初始化数组大可不必使用两重for循环。
•
1) 如果是int数组,采用memset(g, 0x7f, sizeof(g))可全部初始化为一个很大的数(略小于0x7fffffff),使用
图论算法介绍
if (a[i,k]=1)and (a[k,j]=1) then a[i,j]=1 (a[i,j]=1表示i可达j,a[i,j]=0表示i不可达j)。
var
link,longlink:array[1..20,1..20] of boolean;{ 无向图和无向图的传递闭包。其
中
l o n g l i n k[i,
例如:公路交通图,边以距离w为权。
例
2
2
1
3
1
3
有向完全图 例
245
无向完全图 5
1
例 1
3
6
图与子图
57
32
46
G2
顶点5的度:3 顶点2的度:4
3
6
例 245
1
3
6
G1
顶点2入度:1 出度:3 顶点4入度:1 出度:0
例
路径:1,2,3,5,6,3 路径长度:5
245
简单路径:1,2,3,5
❖ 图 G = (V, E)
V = 顶点集 E = 边集 = V V的子集
结点集V={a, b, c, d} 边集E={e1, e2, e3, e4, e5} 其中e1=(a, b), e2=(a, c),
e3=(a, d), e4=(b, c), e5=(c, d)。
(一)、计算无向图的传递闭包
v1→v2→v4→v8→v5 →v3→v6→v7
算法结构:
调用一次dfs(i), 可按深度优先搜索 的顺序访问处理结 点i所在的连通分 支(或强连通分 支),dfs(i)的时 间复杂度为W(n2)。 整个图按深度优先 搜索顺序遍历的过 程如下:
显然,为了避免重复访问同一个顶点,必须 记住每个顶点是否被访问过。为此,可设置 一个布尔向量visited[1..n],它的初值为 false,一旦访问了顶点vi,便将visited[i] 置为ture。 图的深度优先搜索是一个递归过程,可以使 用栈来存储那些暂时不访问的邻接点.类似于 树的前序遍历,它的特点是尽可能先对纵深 方向进行搜索,故称之深度优先搜索。
图论及其算法
-44-第五章图与网络模型及方法§1 概论图论起源于18 世纪。
第一篇图论论文是瑞士数学家欧拉于1736 年发表的“哥尼斯堡的七座桥”。
1847 年,克希霍夫为了给出电网络方程而引进了“树”的概念。
1857 年,凯莱在计数烷n 2n 2 C H 的同分异构物时,也发现了“树”。
哈密尔顿于1859 年提出“周游世界”游戏,用图论的术语,就是如何找出一个连通图中的生成圈,近几十年来,由于计算机技术和科学的飞速发展,大大地促进了图论研究和应用,图论的理论和方法已经渗透到物理、化学、通讯科学、建筑学、生物遗传学、心理学、经济学、社会学等学科中。
图论中所谓的“图”是指某类具体事物和这些事物之间的联系。
如果我们用点表示这些具体事物,用连接两点的线段(直的或曲的)表示两个事物的特定的联系,就得到了描述这个“图”的几何形象。
图论为任何一个包含了一种二元关系的离散系统提供了一个数学模型,借助于图论的概念、理论和方法,可以对该模型求解。
哥尼斯堡七桥问题就是一个典型的例子。
在哥尼斯堡有七座桥将普莱格尔河中的两个岛及岛与河岸联结起来问题是要从这四块陆地中的任何一块开始通过每一座桥正好一次,再回到起点。
当然可以通过试验去尝试解决这个问题,但该城居民的任何尝试均未成功。
欧拉为了解决这个问题,采用了建立数学模型的方法。
他将每一块陆地用一个点来代替,将每一座桥用连接相应两点的一条线来代替,从而得到一个有四个“点”,七条“线”的“图”。
问题成为从任一点出发一笔画出七条线再回到起点。
欧拉考察了一般一笔画的结构特点,给出了一笔画的一个判定法则:这个图是连通的,且每个点都与偶数线相关联,将这个判定法则应用于七桥问题,得到了“不可能走通”的结果,不但彻底解决了这个问题,而且开创了图论研究的先河。
图与网络是运筹学(Operations Research)中的一个经典和重要的分支,所研究的问题涉及经济管理、工业工程、交通运输、计算机科学与信息技术、通讯与网络技术等诸多领域。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
A L F
M B H K G I D E C
由深度优先生成树可得出两类关节点的 特性:
(1) 若生成树的根有两棵戒两棵以上的子树,则此 根顶点必为关节点。因为图中丌存在联结丌同子树
பைடு நூலகம்
中顶点的边,因此,若删去根顶点,生成树便变成
生成森林。如上图中的顶点A。 (2) 若生成树中某个非叶子顶点v,它的子树中的任 一结点均没有指向v的祖先的回边,则v为关节点。因 为,若删去v,则其子树和图的其他部分就被分割开
通分量。退栈到u=v为止,{6}为一个强连通分量。
返回节点5,发现dfn[5]=low[5]=3,退栈后{5} 为一个强连通分量。
返回节点3,继续搜索到节点4,把4加入堆栈。发现节
点4存在向节点1的回边,节点1还在栈中,所以 low[4]=dfn[1]=1。节点6已经出栈,丌再访问6, 返回3,(3,4)为树边,所以low[3]=low[4]=1。
A
A
B
L
F
C
D
E M
F
G
H
J
B
I
J
K
H K
D
E
C
L
连通图G
M
G I
G的深度优先生成树
树边:深度优先搜索树中的 实线表示树边,在DFS过程 中,从v点访问u点时,若u 没有被访问过,则边(v,u) 为树边。
回边:深度优先搜索 树中的虚线表示回边, 在DFS过程中,从v点 访问u点时,若u点已 经访问过,则边(v,u) 为回边。
广度优先搜索
由BFS得到的路径是原图中从S到T的边数 最少的路径 广度优先搜索树不唯一
广度优先搜索的例子
0
0 0 1 1
1
1
4 4
2
2 2
1
3 3
2
5 5
2
6 6 7 7
4
3
8 8
t = 0; void BFS( int s ) // 从 s 开始遍历 { int queue[maxn], tail, head; head = tail = 0; queue[head] = s; depth[s] = 0; time[s] = t++; visit[s] = true; while ( head <= tail ) // queue not empty { int curr = queue[head++]; int i; for (i = 0; i < n; i++) if ( adj[curr][i] && !visit[i] ) { visit[i] = true; depth[i] = depth[curr] + 1; time[i] = t++; queue[++tail] = i; } } }
void solve() { int i; Stop=Bcnt= times =0; memset(dfn,0,sizeof(dfn)); for (i=1;i<=n;i++) if (!dfn[i]) tarjan(i); }
从节点1开始DFS,把遍历到的节点加入栈中。搜索到
节点u=6时,dfn[6]=low[6]=4,找到了一个强连
中存在指向i祖先的回边。反之,若对于某个顶点v,存 在孩子结点w,且low[w]>=dfn[v],表明w及其子 孙均无指向v的祖先的回边,则该顶点v必为关节点。 具体算法如下:
void dfs(int v) { int i,w,cnum=0; //cnum表示孩子个数 times++; visit[v]=1; dfn[v]=low[v]=times; //记录时间戳 for(i=0;i<map[v].size();i++) { w=map[v][i]; if(!visit[w]) //w没有访问过,则w是v的孩子结点 { cnum++; dfs(w); low[v]=min(low[w],low[v]); if(v==root&&cnum==2) //如果v是根,且有2个以上的孩子,则为关节点 flag[v]=1; if(v!=root&&low[w]>=dfn[v])//丌为根若low[w]>=dfn[v]),则为关节点 flag[v]=1; } else if(v!=w) //w已经访问过了,说明从v到w有一条回边,w是v的祖先 { low[v]=min(low[v],dfn[w]); } }
图
论
(Elementary Graph Algorithms)
图的定义
图是由顶点集合以及顶点间的关系的集合 组成的一种关系的数学表示 G = (V,E) 其中顶点是由有穷非空集合 顶点之间的关系(边)是有穷集合 Path (x , y)表示从x到y的一条单向通路, 它是有方向的
图的分类
有向图:图中的边是有方向的。E (x ,y) 和 E ( y ,x)表示的边不同 无向图:图中的边是没有方向的。 完全图:n个顶点的图两两连边,即有 n(n-1)/2条边,则此图为n的完全图。 用K n表示
图的一些概念
邻接顶点:如果(u ,v) ∈E(G),则称u与v互 为邻接顶点 子图:设有两个图G(V,E)和G’(V’,E’),若V’ V 且E’ E’,则称图G‘是图G的子图 权:某些图的边上标有相关的数字,称为 该边的权值
深度优先搜索
每个顶点的d[u] < f[u] DFS中,v是u的子孙 d[u]<d[v]<f[v]<f[u]在搜索中发现u时可 以从u出发沿一条完全由白色顶点组成的路 径到达v m条边的连通图,DFS中顺序标号每条边为 1~m,则任意与顶点u关联的所有边(边数 >=2)的标号的GCD为1
桥:图中的桥(bridge)是一条边,如果删除这条边,将 把连通图分离成两个断开的子图。无桥的图称作边连通图 (edge-connected graph)。
0 6 1 2 10 3 5 4 9 7 8 这个图丌是边连通图。 边0-5、6-7和1112(带阴影)为桥。
11
12
在任何DFS树中,当且仅当丌存在回边连通w的子孙
0
6 1 2 10 3 5 4 9 顶点0、4、5、6、 7和11为关节点。 12 7 8
11
割点、割边以及连通分量
时间戳:dfn[i]表示结点i是第dfn[i]个被访问到的结点。有的时候我 们 还要记录某个结点被遍历并检查完毕的时间。
void dfs(v) { dfn[v]=++times; //记录访问结点的时间戳 visit[v]=1; for 寻找一个v的相邻节点u if (visit[u]==0)then DFS(u); }
顶点的度 一个顶点V的度是与它相连边的条数, 记为Deg(V). 顶点的入度 一个顶点V的入度是以它为终点有向 边的条数,记为InDeg(V) 顶点的出度 一个顶点V的入度是以它为起点有向 边的条数,记为OutDeg(V) 路径 在图 G=(V, E) 中, 若从顶点 vi 出发, 沿 一些边经过一些顶点 Vp1, Vp2, …, Vpm,到达 顶点Vj。则称顶点序列 (Vi Vp1 Vp2 ... Vpm Vj) 为从顶点Vi 到顶点 Vj 的路径。它经过的边(Vi, Vp1)、(Vp1, Vp2)、...、(Vpm, Vj) 应是属于E的 边。
不w的祖先时,树边v-w是一座桥。 不割点类似的,我们定义low[]和dfn[]。父子边
e=u→v ,当且仅当low[v] > dfn[u]的时候,e是
割边。
有向图的强连通分量
在有向图G中,如果两个顶点间至少存在一条路径,称两
个顶点强连通(strongly connected)。如果有向图G
的每两个顶点都强连通,称G是一个强连通图。非强连通 图有向图的极大强连通子图,称为强连通分量(strongly connected components)。 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点 1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。
深度优先搜索
相关概念
递归实现 结点颜色:
白色(开始),灰色(发现),黑色(结束)
一个结点总是从白色变为灰色,再变为黑色 当所有点都变为黑色时,遍历结束 时间戳(timestamp): d[u]表示一个结点开始被访 问的时间,f[u]表示一个结点结束访问的时间
深度优先搜索
int timestamp = 0; dfs(int p) { timestamp = timestamp + 1; col[p] = GREY; d[p] = timestamp; for (每个与p相邻的点i) if (col[i] == WHITE) dfs(i); timestamp = timestamp + 1; f[p] = timestamp; col[p] = BLACK; }
1 0 0 0 1 0 0 1 1 0 1 0
0
1
2
0 1
0 1 0
G.edge[i][j]= 无向图的临界矩阵是对称的
1 0 0 0
有向图的邻接矩阵往往是不对称的
图的存储表示 邻接表(Adjacency List)
Data adj
0
dest link
1 0 0 0 ∧
dest link
2
dest link
3 ∧
0
1
1
3
2
2
3 2
∧ ∧
3
同一个顶点发出的边链接在同一个边链表 中,每一个链结点代表一条边,结点中另 一个顶点的下表dest和指针link
图论相关算法