1. 图的存储表示
1.1. 邻接表表示法
对图中每个顶点,维护一个以该顶点为起点的终点的链表。
整个图需要|V|条链表。邻接表即为这|V|条链表组成的表。
场景:一般用于稀疏表。
优点:
Ø 有向图中,所有邻接表的链长度和为|E|;无向图中,所有邻接表的链长度和为2*|E|.
Ø 每条链表的存储顺序没有要求。
Ø 邻接表表示法中,存储空间为O(V+E)。
Ø 邻接表也可以用于表示带权图,只要将权重存储在链表的顶点v的卫星数据中即可。
缺点:
Ø 要判断一条边(u,v)是否在图中,则需要遍历顶点u的链表。
1.2. 邻接矩阵表示法
为了快速的判断一条边(u,v)是否在图中,可以使用邻接矩阵表示法。
图的邻接矩阵表示法中,将顶点进行编号1.2….|V|。则图G的邻接矩阵A为
|V|*|V|矩阵aij=1,如果边(i,j)存在,aij=0,如果边(i,j)不存在。
特点:
Ø 存储空间为O(|V|*|V|),只与顶点有关,与边无关。
Ø 无向图中邻接矩阵为对称矩阵。因此存储空间可以减少一半。
Ø 如果以aij表示带权图的权,则也可以表示带权图。如果边不存在,则以无穷大表示。
2. 图的搜索
图的搜索是指有序的沿着图的边访问所有顶点。
2.1. 广度优先搜索(BFS)
给定图G=(V,E)及任意一个源顶点s,广度优先搜索系统地搜索图G中的边,以便“发现”可以从s到达的所有顶点,并计算s到达这些顶点的距离(最少的边数)。
该算法还能生成一颗以s为根,且包含s可达的所有顶点的一颗广度优先树。
从s可达的任何顶点v,广度优先树中,从s到v的路径对应于图G中从s到v的一条最短路径。(对有向图,无向图都一样)。
算法称为广度优先的原因在于,算法始终以已发现、未发现顶点之间的边界,沿着广度方向向外发展。即算法会先发现与s相距为k的所有顶点,再发现距离为k+1的所有顶点。
广度优先搜索中,对顶点标以不同的颜色:白色、灰色、黑色。
白色表示未发现的点,灰色、黑色都是已经发现的点,区别在于黑色表示其相邻顶点都已发现,但灰色顶点可能还有未发现的邻接点。即灰色代表了已发现和未发现的边界。
广度搜索构造了一颗广度优先树。最开始只包含根,即源顶点s。在扫描已发现的顶点u时,如果发现一个白色顶点v,则将该顶点v以及边(u,v)添加到树中。称u为v的父节点。
使用邻接表表示法时,广度优先搜索可以更快。因为和已发现顶点u的邻接点在其邻接链表中。
对每个顶点,定义color域代表颜色,定义p域代表父节点,定义d域代表和源顶点s的距离。
算法中使用一个优先队列(先进先出队列)管理灰色顶点,使用个迭代循环完成。
算法循环从队列中取顶点u,并从u的邻接表中取邻接顶点,如果邻接顶点为白色,则代表新发现,因此置为灰色。计算其d,p域,并入队列。链表处理完后,将u置为黑色。
循环直到队列中无灰色元素为止。
复杂度:为O(V+E)。
灰色队列中,任何时刻,队头的元素的d域最多比队尾的d域小1,且队列中的d域为非递减的。
广度优先树也称作前驱子图。在搜索的时候即可建立。
扩展:
BFS搜索时,如果图以邻接矩阵表示。如何进行搜索?复杂度是多少?
树的直径:表示为树中所有最短路径的最大值。求树的直径。
2.2. 深度优先(DFS)
深度优先算法是尽可能“深”的搜索一个图。在深度优先算法中,对于新发现的顶点v,如果还有以此顶点v为起点的边,就沿着边继续探测下去。当该顶点v的所有边都探测完后,再回到发现顶点v的边的起始点。重复过程,直到从源顶点可达的所有顶点都被访问。如果此时还有未发现顶点,则选择一个为源顶点,重复这个过程,直到所有顶点都被访问,这是和广度优先算法的不同点,这个不同的设计主要在于这两种算法的用法不同,广度优先一般用于查找最短路径,深度优先一般作为一个子程序,用于在图中遍历所有节点,以便进行别的操作。
因此,深度优先遍历出来的是深度优先森林,与广度优先树不同,深度优先森林。
深度优先的做法也是对顶点进行标色,标色方法和广度类似。且一般再对每个顶点标上两个时间,顶点v被发现的时间d(v)(变灰的时间),以及对v为根的树完成搜索的时间f(v)(变黑的时间)。因此在d(v)前,v为白色,d(v)和f(v)之间为灰色,f(v)后为黑色。
算法对图进行遍历,先初始化,将所有顶点标识为白色,且父为空。对每个顶点,如果为白色则深度优先访问该顶点。
深度优先访问顶点时,先将顶点值灰,并设置d(v),再对v的邻接点进行遍历,如果发现一个白色(未访问的顶点),则设置其父信息,并先递归对邻接的进行访问。当所有邻接点都访问完后,再将v置为黑色,并设置f(v)。递归为尾递归,因此可以用一个栈来消掉递归调用。
复杂度:也为O(V+E)。
深度优先性质:
Ø (括号定理)由[d(v),f(v)]组成的时间戳满足三分关系:两个时间的时间戳不相交,这时候这两个顶点u,v在深度优先森林中都不是对方的后裔。u的时间戳包含v的,则u为v的父。v的时间戳包含u的,则v为u的父。
Ø (后裔嵌套)顶点u为v的父,当且仅当u时间戳包含v的时间戳。
Ø (白色定理)顶点u为v的父,当且仅当在d(u)时刻发现u时,可以从u出发,经过由白色顶点组成的路径到达v。
深度优先树中边的分类:
Ø 树边:深度优先森林中,顶点v是在搜索边(u,v)时发现的,则(u,v)即为树边。
Ø 反向边:深度优先森林中,连接顶点u到其某一祖先(非父)顶点的边。有向图中的自环也是反向边。
Ø 正向边:顶点u与其后裔v的非树边(即u与孙节点v以及孙孙节点的边)。
Ø 交叉边:深度优先森林中,同一颗树中,一个顶点不是另一个顶点的祖先。或者不同树的顶点之间的边。
DFS入度非0的顶点开始访问时,d,f的关系不再成立,森林也有问题,拓扑排序也有问题?
2.3. 拓扑排序
有向无环图才能进行拓扑排序。否则不能排序。
是否可以拓扑排序:有向图无回路,当且仅当深度优先搜索时,无反向边则可以拓扑排序。
拓扑排序方法:
方法一,使用深度优先搜索,并在完成搜索后,将顶点插入到一个链表的头部,搜索完成后,链表即为拓扑排序的结果。
方法二,找入度为0的顶点,删除顶点以及边;循环找入度为0的并删除顶点即边,直到无顶点。(生成树为一棵树的图可行,关系的保留问题。多生成树时的处理。)
2.4. 强连通分支
连通图:无向图,且任何两个顶点之间都“可达”,不需要直接可达。
连通分量:无向图的极大连通子图。
无向图连通分量,通过图的遍历即可求解。
强连通图:有向图,且任何两个顶点之间都“可达”,不需要直接可达。
强连通分量:有向图的极大强连通子图。
图的转置图。
强连通分量为图和转置图中都互相可达。
有向图强连通分量,通过遍历两遍,一遍为图,一遍为转置图。