G Graph V Vertex E Edge
图是由顶点集V与边集E组成,记为G=(V,E),
V(G)表示图G中顶点的有限非空集;E(G)表示图G中顶点之间的关系(边)集合。
若V={v1,v2…vn},则用|V|表示图G中顶点的个数,即图G的阶。
若E={(u,v)|u∈U,v∈V},则用|E|表示图G中边的条数。
注意:
线性表可以是空表,树也可以是空树,但图不可以是空图,
即V(顶点集)一定是非空集,边集可以是空集
如铁路网络
微信(无向图)-》无向边(边)记为(v,w)或(w,v)
微博(有向图)-》有向边(弧)记为
对于无向图:顶点v的度是指依附于该顶点的边的条数,记为TD(v)。
对于有向图:
入度是以顶点v为终点的有向边的数目,记为ID(v)。
出度是以顶点v为起点的有向边的数目,记为OD(v)。
顶点v的度TD(v)=ID(v)+OD(v).
注意:
对于无向图:所有顶点的度之和 应该是等于这个无向图里面 边 的数量 乘于2
对于有向图:所有顶点的入度之和与出度之和是相等的且等于弧的这个条数。
路径:顶点Vp到顶点Vq之间的一条路径,顶点序列,
回路:第一个顶点和最后一个顶点相同的路径,回路或环
简单路径:在路径序列中,顶点不重复出现的路径称为简单路径。
简单回路:除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路。
路径长度:路径上 边 的数目
点到点的距离:从顶点u出发到顶点v的最短路径若存在,则此路径的长度称为从u到v的距离。若从u到v根本不存在路径,则记该距离为无穷。
无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的
有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的
无向图,若图G中任意两个顶点都是连通的,则称图G为连通图,否则称为非连通图
有向图,若图中任何一对顶点都是强连通的,则称此图为强连通图。
注意:
对于n个顶点的无向图G,
若G是连通图,则最少有n-1条边
若G是非连通图,则最多有(Cn-1^2)条边
注意:
对于n个顶点的有向图G,
若G是强连通图,则最少有n条边。(形成回路)
设有两个图G=(V,E)和G’=(V’,E’)
若V’是V的子集,且E’是E的子集,则G’是G的子图。
若有满足V(G’)=V(G)的子图G’,则称其为G的生成子图
有向图 同理可得
无向图的极大连通子图称为连通分量(子图必须连通,且包含尽可能多的顶点和边)
每一个连通分量都是原图的一个子图,并且都是连通的
eg 将中国铁路客运线路图 分为 大陆铁路网 海南岛铁路网 台湾岛铁路网三个连通分量
(无向图)
连通图的生成树是包含图中全部顶点的一个极小连通子图
(边尽可能少)
该生成树中 若顶点为n,则边为n-1
若顶点为n,边为n,则构成回路
若顶i点为n,边为n-,则构成非连通图
生成树保证顶点都是连通的
边的权-:在一个图中,每一条边都可以标上具有某种含义的数值,该数值称为该边的权值
带权图/网:边上带有权值的图称为带权图
带权路径长度:当图是带权图时,一条路径上所有边的权值之和,称为该路径的带权路径长度·
倘若是有向图,即可给各边赋予一个数值 eg:转发概率
**无向完全图 ** :无向图中任意两个顶点都存在边
有向完全图:有向图中任意两个顶点之间都存在方向相反的弧
稀疏图:边很少的图
反之,
稠密图
树:不存在贿赂,且连通的无向图
有向树:一个顶点的入度为0,其余顶点的入度均为1的有向图,称有向树
注意;相同的图 邻接矩阵的表示方式唯一
数组实现的顺序存储,空间复杂度高,不适合存储稀疏图
1表示两顶点连接,0表示两顶点不连接
有向图中 a行b列 由a指向b的弧
定义一个二维数组
eg:edge[a][b]=0;//从a顶点到b顶点存在 边
无向图:第i个节点的度=第i行(或第i列)的非0元素的个数 O(n)
有向图:
第i个节点的出度=第i行的非0元素的个数 O(n)
第i个节点的入度=第i列的非0元素的个数 O(n)
第i个节点的度=第i个节点的出度+第i个节点的入度
倘若该图 有n个顶点 则需要O(n)级别的空间复杂度来存储该图的顶点
存储边需要n*n个空间, 则需要O(n^2)级别的空间复杂度来存储该图的边
即:
邻接矩阵的空间复杂度与顶点有关(无论边数是否存在都存储)–适合存储稠密图
注意:由于无向图的邻接矩阵是对称矩阵,可以压缩存储(只存上三角或下三角)
图为压缩矩阵
a11a14:a的1行1列a的1行4列=00
:向量AA向量AD=向量AD=0即不存在
a12a24:a的1行2列a的2行4列=11
:向量AB向量BD=向量AD=1存在
…
看右下角图:从B到B长度为2的路径有3条
看右下角图:从B到B长度为3的路径有2条
无向图:data存储顶点信息,*first存储边信息 边结点信息会存两份,如(a,b)会存(a,b)和(b,a)_虽然两者是同一条边 _ , 所以整体空间复杂度为O(|V|+2|E|)
有向图:整体空间复杂度为O(|V|+|E|)
无向图:遍历 data顶点对应的first 即可得出度
有向图:遍历data顶点对应的first 即可得出 出度,依次遍历所有的data顶点的对应的*first 找出指向所有求data的数量 即可得出 入度 度=出度+入度
(时间复杂度较高)
过于复杂 暂不学习
过于复杂 暂不学习
无向图——邻接矩阵:找到x行y列对应的元素是否为1 O(1)
无向图——邻接表:O(1)~O(|V|)
有向图——邻接矩阵O(1)
有向图——邻接表:O(1)~O(|V|)
无向图——邻接矩阵:遍历x行x列所有的对应的元素是否为1 O(|V|)
无向图——邻接表:遍历x边结点相连的链表 O(1)~O(|V|)
有向图——邻接矩阵 O(|V|)
有向图——邻接表:出边O(1)~O(|V|) 入边O(|E|)
无向图——邻接矩阵:添加x行x列元素值都设为0 O(1)
无向图——邻接表:表末尾插入data 指针为null O(1)~O(|V|)
有向图——邻接矩阵 添加x行x列元素值都设为0 O(1)
有向图——邻接表:表末尾插入data 指针为null O(1)~O(|V|)
无向图——邻接矩阵:将x行x列元素值都设为0,添加标记将x标记为null O(|V|)
无向图——邻接表:删除边x,与边相邻的指针之外,还得找其他所有的边中是否有与相连的指针 O(1)~O(|E|)
有向图——邻接矩阵 添加x行x列元素值都设为0 O(|V|)
有向图——邻接表:删除边x,与边相邻的指针 删除出边 O(1)~O(|V|) 删除入边(|E|)
无向图——邻接矩阵:添加x行x列元素值都设为1 O(1)
无向图——邻接表:找到指定data, 指针为后增加指针 O(1)~O(|V|)
有向图——邻接矩阵:添加x行x列元素值都设为1 O(1)
有向图——邻接表:找到指定data, 指针为后增加指针 O(1)~O(|V|)
无向图——邻接矩阵:找x行元素值第一个为1,则返回列号 O(1)~O(|V|)
无向图——邻接表:找到边结点第一个结点后第一个指针 O(1)
有向图——邻接矩阵:找x行元素值第一个为1,则返回列号 出行 O(1)~O(|V|)
有向图——邻接表:找到边结点第一个结点后第一个指针 出边 O(1)遍历所有边结点,第一个结点后第一个指针 ~O(|V|)
获取图G中边(x,y)或
设置图G中边(x,y)或
与
判断某一条边是否存在相同
方法类似
树本身就是一种特殊的图 树的广度优先遍历(层序遍历)
树的广度优先遍历:从根结点出发,找到和根结点相邻的所有节点,接下来从234出发找到与其相邻的所有结点……【横向的找】
图的广度优先遍历:例如从2结点出发,找到和2结点相邻的所有节点,接下来从1 6出发找到与其相邻的所有结点……【横向的找】
通过某个结点找到与之相邻的其他结点
由于图会产生回路(树不可能产生回路)因此,需要给节点添加标记
1.找到与一个顶点相邻的所有顶点————使用FirstNeighbor(G,x):找到顶点的邻接点,NextNeighbor(G,x,y),找到除y之外的顶点x的下一个邻接点的顶点
2.定义bool visited[MAX_VERTEX_NUM]; 标记是否访问过
请注意:
使用邻居接表存储,广度优先遍历序列的结果可能会不同
使用邻接矩阵存储,广度优先遍历序列的结果唯一
对于最终版,空间复杂度主要来自于辅助队列
最坏情况下O(|V|)
邻接矩阵存储的图:时间复杂度为 O(|V^2|)
邻接表存储的图:时间复杂度为 O(|V|+|E|)
红色的线为n-1条 共有n个顶点
若只有红线,则变成广度优先生成树
树的深度优先遍历 分为先根遍历、后根遍历
图的深度优先遍历类似于树的先根遍历
先访问一个节点,然后再用一个循环来依次检查和这个节点相邻的其他节点,然后再进行更深一层的访问
空间复杂度:来自函数调用栈,最坏情况,递归深度为O(|V|)
时间复杂度=访问各个结点所需要时间+探索各边所需要时间
邻接矩阵存储的图:
查找每个顶点的邻接点都需要O(|V|)的时间,而总共有|V|个顶点
时间复杂度=O(|V|²)
邻接表存储的图:
访问|V|个顶点需要|V|个时间
查找每个顶点的邻接点都需要O(|E|)的时间
时间复杂度=O(|E|+|V|)
同一个图的邻接矩阵表示方式唯一,因此深度优先遍历序列唯一
同一个图的邻接表表示方式不唯一,因此深度优先遍历序列不唯一
若一个图是非连通的 需要多次调用DFS函数(深度优先遍历)
每调用一次DFS,则可生成一个深度优先生成树
多个树,就构成森林
对无向图进行BFS/DFS遍历
调用BFS/DFS函数的次数=连通分量数=生成树的数量
对于连通图,则只需要调用一次BFS/DFS
对有向图进行BFS/DFS遍历
调用BFS/DFS函数的次数具体问题具体分析
对于连通图,若起始顶点到其他各顶点都有路径,则只需要调用1次BFS/DFS
若有向图是强连通图,则从任一顶点出发都只需要调用1次BFS/DFS
连通图的生成树:包含图中全部顶点的一个极小连通子图。
带权连通图可能有多个生成树,从其所有的生成树当中,找到各边权值之和最小的生成树。
最小生成树可能有多个,但边的权值之和总是唯一且最小的
最小生成树的边数=顶点数-1.————去掉一个边构不成连通,增加一条边出现回路
注意:
如果一个连通图本身就是一颗树,则其最小生成树就是其本身
只有连通图才有生成树,非连通图只有生成森林
从某一个顶点开始构建生成树;每次把代价最小的新顶点纳入生成树,直到所有顶点都纳入为止。
第一轮处理,循环遍历所有结点,找到lowCast最低的,且没有加入树顶点(lowCost距离树的各个顶点的最短距离)
第二轮处理,循环遍历所有结点,找到lowCast最低的,且没有加入树顶点(
再次循环遍历,更新还没有加入各个顶点的lowCast值)
……
总时间复杂度为O(n²),即O(|V|²)
每次选择一条权值最小的边,使这条边的两头连通(原本连通的不选),直到所有结点都连通
第一轮:检查第一条边的两个顶点是否连通(是否属于同一集合)**【并查集】,不连通,连通起来
第二轮:检查第二条边的两个顶点是否连通(是否属于同一集合){vo,v3}是一个集合,**不连通,连通起来
……
总时间复杂度为O(elog2 e)
单独一个源头,从该源头出发,到达其他任意一个顶点可以走的最短路径-单源最短路径
单独一个源头,从该源头出发,到达其他任意一个顶点可以走的最短路径-单源最短路径
BFS算法的局限性 :无法适用于带权图
带权路径长度:当图是带权图时,一条路径上所有边的权值之和,称之为该路径的带权路径长度,简称路径长度
eg:求vo点到其他各个顶带点的最短路径:
先初始化3个数组,
标记各个顶点是否已经找到最短路径的数组final【5】:表示到目前为止是否找到从v0到这些顶点的最短路径
最短路径长度的数组dist【5】:目前为止能够找到的最短的最优的一条路径的长度
路径上的前驱的数组path【5】:用于记录每一个顶点在最短路径上的直接前驱
三个数组与顶点一一对应
第一轮:循环遍历所有结点,找到还没确定的最短路径,且dist最小的Vi(V4),令final[i]=true
检查所有与vi(v4)相连的顶点,更新dist【5】与path【5】中的数据;
第二轮:循环遍历所有结点,找到还没确定的最短路径,且dist最小的Vi(V3),令final[i]=true
检查所有与vi(v3)相连的顶点,更新dist【5】与path【5】中的数据;
……
最终
V0到V2的最短带权路径长度为dist[2]=9
通过path【】可知,V0到V2的最短带权路径:v2—v1–v4–v0
注意:distance的dist【】和prim的lowcost【】两个数组的作用类似
注意:如果存在负权值的边,distance算法就会**失效,**找不到最短的带权路径
各个顶点的之间的最短路径(动态规划)、
初始:不允许中转
两个初始的矩阵(二维数组),第一个矩阵为该图的邻接矩阵,为就目前来看,各个顶点的最短路径长度 第二个矩阵为目前能找到的最短路径当中两个顶点之间的一个中转点:(刚开始,所有的顶点之间都不可以有中转点,所以会将所有的这些path值设置为-1 )
#v0:若允许在v0中转
由初始阶段的两个矩阵的信息求该阶段的信息 (遍历上一阶段留下来的这个矩阵A ,对于矩阵A中每一个具体的元素,我们都需要进行一次检查)
#1:若允许在v0,v1中转
……
时间复杂度:O(|V|²)
空间复杂度:O(|V|²)
弗洛伊德算法可以解决带负权值图的问题,但解决不了带有负权回路的图
有向无环图(DAG)
若一个有向图中不存在环路,则称为有向无环图
有向无环图(DAG)
若一个有向图中不存在环路,则称为有向无环图
DAG图的第二个应用——拓扑排序
AOV网(用顶点表示活动结构的网):
用DAG网(有向无环图)表示一个工程。顶点表示活动,有向图
拓扑排序:在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序:
①每一个顶点出现且只出现一次。
②若顶点A在序列中排在顶点B的前面,则在图中不存在从顶点B到顶点A的路径。
或定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得若存在一条从顶点A到顶点B的路径,则在排序中顶点B出现在顶点A的后面。每一个AOV网都有一个或多个拓扑排序序列。
(拓扑排序:找到做事的先后顺序)
拓扑排序的实现:
①从AOV网中选择一个没有前驱(入度为0)的顶点并输出。
②从网中删除该顶点和所有以它为起点的有向边。
③重复①和②直到当前的AOV网为空或当前网中不存在无前驱的顶点为止。
注意:若原图存在回路,则无法进行拓扑排序
时间复杂度:O(|V|+|E|)
若采用邻接矩阵,则需要O(|V|²)
对于一个AOV网,如果采用下列步骤进行排序,则称之为逆拓扑排序:
①从AOV网中选择一个没有后继(出度为0)的顶点并输出。
②从网中删除该顶点和所有以它为终点的有向边。
③重复①和②直到当前的AOV网为空。
逆邻接表(指向顶点的)
AOE网:在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需时间),称之为用边表示活动的网络,简称AOE网
①只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始。
②只有在进入某顶点的各有向边所代表的活动都已经结束时,该顶点所代表的事件才能发生。
另外,有些活动是可以并行进行的
在AOE网中仅有一个入度为0的顶点,称之为开始顶点(源点),它表示整个工程的开始;
也仅有一个入度为0的顶点,称之为结束顶点(汇点),它表示整个工程的结束。
从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称之为关键路径,而把关键路径上的活动称之为关键活动
完成整个工程的最短时间就是关键路径的长度,若关键活动不能按时完成,则整个工程的完成时间就会延长
事件Vk的最迟发生时间——它是在不推迟整个工程完成的前提下,该事件最迟必须发生的时间。
活动的最迟开始时间——它是指该活动弧的终点所表示事件的最迟发生时间于该活动所需时间之差。
若边
关键活动:a2、a5、a7
关键路径:V1》V3》V4》V6
若关键活动耗时增加,则整个工程的工期将增长
缩短关键活动的时间,可以缩短整个工程的工期
当缩短到一定程度时,关键活动可能会变成非关键活动
可能存在多条关键路径,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的