图
图
图的基本概念
定义:树是N(N≥0)个结点的有限集合,N=0时,称为空树,这是一种特殊情况。在任意一棵非空树中应满足:1)有且仅有一个特定的称为根的结点。2)当N>1时,其余结点可分为m(m>0)个互不相交的有限集合T1,T2,…,Tm,其中每一个集合本身又是一棵树,并且称为根结点的子树。
图G由顶点集V和边集E组成,记为G=(V,E)
V(G)表示图G中顶点的有限非空集。用|V|表示图G中顶点的个数,也称为图G的阶
E(G)表示图G中顶点之间的关系(边)集合。用|E|表示图G中边的条数。
分类
有向图
有向边(弧)的有限集合
弧是顶点的有序对
<v,w>
v是弧尾,w是弧头
v邻接到w或w邻接自v
无向图
无向边的有限集合
边是顶点的无序对
(v,w)
(v,w)=(w,v)
w,v互为邻接点
简单图
1.不存在顶点到自身的边
2.同一条边不重复出现
多重图
- 若图G中某两个结点之间的边数多于一条,又允许顶点通过通过同一个边和自己关联
完全图
无向完全图
- 如果任意两个顶点之间都存在边
有向完全图
- 如果任意两个顶点之间都存在方向相反的两条弧
子图
连通图:图中任意两个顶点都是连通的
连通分量:无向图中的极大连通子图
连通
- 顶点A到顶点B有路径
极大
1.顶点足够多
2.极大连通子图包含这些依附这些顶点的所有边
结论1:如果一个图有n个顶点,并且有小于n-1条边,则此图必是非连通图。
概要: 找连通分量的方法:从选取一个顶点开始,以这个顶点作为一个子图,然后逐个添加与这个子图相连的顶点和边直到所有相连的顶点都加入该子图
强连通:顶点V到顶点W和顶点W到顶点V都有路径
强连通图:图中任一对顶点都是强连通的
连通图的生成树:包含图中全部n个顶点,但是只有n-1条边的极小连通子图
- 结论2:生成树去掉一条边则变成非连通图,加上一条边就会形成回路。
度:以该顶点为一个端点的边数目
无向图中顶点V的度是指依附于该顶点的边的条数,记为TD(v)
有向图中顶点V的度分为出度和入度
入度(ID)是以顶点v为终点的有向边的数目
出度(OD)是以顶点V为起点的有向边的数目
简单路径和简单回路:顶点不重复出现的路径称为简单路径。对于回路,除了第一个和最后一个顶点其余顶点不重复出现的回路称为简单回路
权和网:图中每条边考研赋予一定意义的数值,这个数值叫做这条边的权,有权值得图称为带权图,也叫做网
路径和路径长度:顶点p到q之间的路径是指顶点序列怕保存的,p,a,b,c,d,……q。路径上边的数目就是路径长度
回路(环):第一个和最后一个顶点相同的路径称为回路或者环
距离:从顶点u到v的最短路径长度。不存在路径则为无穷
图的存储结构
邻接矩阵(顺序存储)
邻接表(链式存储)
十字链表(有向图)
邻接多重表(无向图)
图的遍历
深度优先遍历
深度优先搜索(DFS:Depth-First-Search):深度优先搜索类似于树的先序遍历算法
空间复杂度:由于DFS是一个递归算法,递归是需要一个工作栈来辅助工作,最多需要图中所有顶点进栈,所以时间复杂度为O(|V|)
时间复杂度:1)邻接表:遍历过程的主要操作是对顶点遍历它的邻接点,由于通过访问边表来查找邻接点,所以时间复杂度为O(|E|),访问顶点时间为O(|V|),所以总的时间复杂度为O(|V|+|E|) 2)邻接矩阵:查找每个顶点的邻接点时间复杂度为O(|V|),对每个顶点都进行查找,所以总的时间复杂度为O(|V|2)
广度优先遍历
广度优先搜索(BFS:Breadth-First-Search):广度优先搜索类似于树的层序遍历算法
空间复杂度:BFS需要借助一个队列,n个顶点均需要入队一次,所以最坏情况下n个顶点在队列,那么则需要O(|V|)的空间复杂度。
时间复杂度:1)邻接表:每个顶点入队一次,时间复杂度为O(|V|),对于每个顶点,搜索它的邻接点,就需要访问这个顶点的所有边,所以时间复杂度为O(|E|)。所以总的时间复杂度为O(|V|+|E|)2)邻接矩阵:每个顶点入队一次,时间复杂度为O(|V|),对于每个顶点,搜索它的邻接点,需要遍历一遍矩阵的一行,所以时间复杂度为O(|V|),所以总的时间复杂度为O(|V|2)
图的应用
最小生成树
普利姆(Prlm)
①从图中找第一个起始顶点v0,作为生成树的第一个顶点,然后从这个顶点到其他顶点的所有边中选一条权值最小的边。然后把这条边的另一个顶点v和这条边加入到生成树中。
②对剩下的其他所有顶点,分别检查这些顶点与顶点v的权值是否比这些顶点在lowcost数组中对应的权值小,如果更小,则用较小的权值更新lowcost数组。
③从更新后的lowcost数组中继续挑选权值最小而且不在生成树中的边,然后加入到生成树。
④反复执行②③直到所有所有顶点都加入到生成树中。
概要:
- 双重循环,外层循环次数为n-1,内层并列的两个循环次数都是n。故普利姆算法时间复杂度为O(n2)而且时间复杂度只和n有关,所以适合稠密图
克鲁斯卡尔(Kruskal)
将图中边按照权值从小到大排列,然后从最小的边开始扫描,设置一个边的集合来记录,如果该边并入不构成回路的话,则将该边并入当前生成树。直到所有的边都检测完为止。
概要: * *
- 概要: 克鲁斯卡尔算法操作分为对边的权值排序部分和一个单重for循环,它们是并列关系,由于排序耗费时间大于单重循环,所以克鲁斯卡尔算法的主要时间耗费在排序上。排序和图中边的数量有关系,所以适合稀疏图
最短路径
迪杰斯特拉
一个源点到其余顶点的最短路径
- 该算法设置一个集合S记录已求得的最短路径的顶点,可用一个数组s=1时表示将顶点vi放入S中,初始时把源点v0放入S中。此外,在构造过程中还设置了两个辅助数组:dist初值为arcsv0。path表示从源点到顶点i之间的最短路径的前驱结点,在算法结束时,可根据其值追溯得到源点v0到顶点vi的最短路径。
假设从顶点0出发,也就是顶点0为源点,集合S最初只包含顶点0,邻接矩阵arcs表示带权有向图,arcs[i] [j]表示有向边<i,j>的权值,若不存在有向边<i,j>,则arcs[i] [j]为∞。Dijkstra算法的步骤如下:
1)初始化:集合S初始为{0},dist[] [i]=arcs[0] [i],i=1,2,…,n-1。
2)找出dist[] [j],将顶点j加入集合S,即修改s[vj]=1。
3)修改从v0出发到集合V-S上任一顶点vk可达的最短路径长度:如果dist[j] [j][k] [k],则令dist[k] [j] + arcs[j] [k]。另外更新path[k]=j(也就是顶点j加入集合之后如果有新的路径使得到顶点k路径变短的话就将到顶点k的路径长度修改成较短的)
4)重复2)~3)操作共n-1次,直到所有的顶点都包含在S中。
弗洛伊德
所有顶点到所有顶点的最短路径
- 算法思想:递推产生一个n阶方阵序列A(−1),A(0),…,A(k),…,A(n−1)其中A(k)i表示从顶点vi到顶点vj的路径长度,k表示绕行第k个顶点的运算步骤。初始时,对于任意两个顶点vi和vj,若它们之间存在边,则以此边上的权值作为它们之间的最短路径长度;若它们之间不存在有向边,则以∞作为它们之间的最短路径长度。以后逐步尝试在原路径中加入顶点k(k=0,1,…,n-1)作为中间顶点。如果增加中间顶点后,得到的路径比原来的路径长度减少了,则以此新路径代替原路径
非带权图
- 两点之间经过边数最少的路径
带权图
两点之间经过的边上权值之和最小的路径
- 拓扑排序
AOV
- 如果我们把每个环节看成图中一个顶点,在这样一个有向图中,用顶点表示活动,用弧表示活动之间的优先关系,那么这样的有向图称为AOV网(Activity On Vertex)
拓扑排序就是对一个有向图构造拓扑序列的过程,构造会有两种结果:如果此图全部顶点都被输出了,说明它是不存在回路的AOV网;如果没有输出全部顶点,则说明这个图存在回路,不是AOV网。
拓扑排序算法: 从AOV网中选择一个入度为0的顶点输出,然后删去此顶点,并删除以此顶点为弧尾的弧。重复这个步骤直到输出图中全部顶点,或者找不到入度为0的顶点为止。
关键路径
- AOE(Activity On Edge):在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网称为AOE网。
