数据结构和算法中常用到的就是图,我记性不好总是忘东西,只好把一些琐碎的知识点记下来,方便以后查看。
图(Graph)由表示数据元素的集合V和表示数据之间关系的集合E组成,记为G= <V,E>,V表示顶点,E表示边。图的分类有:有向图、带权图、稀疏图、稠密图、完全图、连通图
完全图
:任何两顶点间都有边相关联,具有最大边数,C(n,2).
简单路径:序列中顶点不重复出现的路径
一个有向图中,若存在一个顶点v0,从此顶点有路径可以到达图中其他所有顶点,则称此有向图为
有根的图,v0称作
图的根
连通分量(connected component)定义为无向图中的极大连通子图。
强连通图:对于有向图G= <V,E>,若G中任意两个顶点v
i和v
j(v
i≠v
j),都有一条从v
i到v
j的有向路径,同时还有一条从v
j到v
i的有向路径
图的存储结构为:相邻矩阵、邻接表、十字链表
十字链表(Orthogonal List)是
有向图的另一种链式存储结构,可以看成是邻接表和逆邻接表的结合。表中对应于有向图的每一条弧有一个表目,共有5个域:头(headvex)和尾(tailvex)分别表示弧头(终点)和弧尾(始点)顶点序号;tailnextarc链接指针指向下一条顶点以tailvex为弧尾的弧;headnextarc指针指向下一条以顶点headvex为弧头的弧;此外还有一个表示弧权值等信息的info域 顶点表目由3个域组成:data域存放顶点的相关信息;firstinarc链接指针指向第一条以该顶点为终点的弧;firstoutarc链接指针指向第一条以该顶点为始点的弧。所有的顶点也可以放在顺序存储结构中
、
图的深度搜索DFS:类似于树的先根序遍历,用邻接矩阵,需要检查O(n^2)次,邻接表,则需要O(n+e)次
图的广度搜索BFS: 类似于树的层次遍历,使用队列
拓扑排序:对有向无环图(DAG)建立拓扑序列的过程叫拓扑排序。主要是解决先决条件问题,以某种线性顺序来组织多项任务。 方法:从有向图中删除一个没有前驱(入度为0)的顶点,并输出它,删除途中该顶点和所有以它为起点的弧,回到第一步继续执行。使用队列实现。
void TopsortbyQueue(Graph&G) {
for(inti= 0;i < G.VerticesNum();i++) // 初始化Mark数组
G.Mark[i]= UNVISITED;
using std::queue; // 使用STL中的队列
queue<int>Q;
for(i= 0; i< G.VerticesNum();i++) // 入度为0的顶点入队
if(G.Indegree[i]== 0)
Q.push(i);
while (!Q.empty()){ // 如果队列非空
intv = Q.front(); // 获得队列顶部元素
Q.pop(); // 队列顶部元素出队
Visit(G,v); // 问顶点v
G.Mark[v]= VISITED; // 将标记位设置为VISITED
for(Edge e = G.FirstEdge(v);G.IsEdge(e);e= G.NextEdge(e)){
G.Indegree[G.ToVertex(e)]–; // 与该顶点相邻的顶点入度减1
if(G.Indegree[G.ToVertex(e)]== 0) // 如果顶点入度减为0则入队
Q.push(G.ToVertex(e));
}
}
for (i = 0; i < G.VerticesNum();i++) // 利用标记位可以判断图中是否有环
if(G.Mark[i]== UNVISITED) {
cout<<“此图有环!“;
break;
}
}
单源最短路径:Dijkstra算法基本思想,每条边至少检查一遍,使用最小堆找最短路径,复杂度O(V+E)lgV,适合稀疏图
—把图的顶点分成两个集合S和V-S。第一个集合S表示最短距离已经确定的顶点集,即一个顶点如果属于集合S当且仅当从源点s到该顶点的最短路径已知
—其余的顶点放在另一个集合V-S中。初始时,集合S只包含源点,即S ={s}。设v是V中的某个顶点,把从源点s到顶点v且中间只经过集合S中顶点的路径称为从源点到v的特殊路径,并用数组D来记录当前所找到的从源点s到每个顶点的最短特殊路径长度
—从尚未确定最短路径长度的集合V-S中取出一个最短特殊路径长度最小的顶点u,将u加入集合S,同时修改数组D中由s可达的最短路径长度
class Dist { //Dist类,Dijkstra和Floyd算法用于保存最短路径信息
public:
intindex; //顶点的索引值,仅Dijkstra算法用到
intlength; //当前最短路径长度
intpre; //路径最后经过的顶点
};
每对顶点之间的最短路径:Floyd算法用相邻矩阵adj来表示带权有向图
**初始化adj(0)为相邻矩阵adj
**在矩阵adj(0)上做n次迭代,递归地产生一个矩阵序列adj(1),…,adj(k),…,adj(n)
**其中经过第k次迭代,adj(k)[i,j]的值等于从顶点vi到顶点vj路径上所经过的顶点序号不大于k的最短路径长度
由于进行第k次迭代时已求得矩阵adj(k-1),那么从顶点vi到顶点vj中间顶点的序号不大于k的最短路径有两种情况:
* 一种是中间不经过顶点vk,那么此时就有adj(k) [i,j] = adj(k-1)[i,j]
* 另一种是中间经过顶点vk,此时adj(k) [i,j] < adj(k-1)[i,j],那么这条由顶点vi经过vk到顶点vj的中间顶点序号不大于k的最短路径由两段组成: •一段是从顶点vi到顶点vk的中间顶点序号不大于k-1的最短路径 •另一段是从顶点vk到顶点vj的中间顶点序号不大于k-1的最短路径 •路径长度应为这两段长度之和,用下面的公式计算:adj(k) [i,j]= adj(k-1)[i,k]+ adj(k-1)[k,j]
最小生成树:
树上所有权值总和表示代价,那么在G的所有的生成树中,代价最小的生成树
MST性质:设G= <V,E>是一个联通的带权图,其中每条边(vi,vj)上带有权W(vi,vj)。集合U是顶点集V的一个非空真子集。构建生成树时需要一条边连通顶点集合U和V-U。如果(u,v)∈E,其中u∈U,v∈V-U,且边(u,v)是符合条件的权值W(u,v)最小的,那么一定存在一棵包含边(u,v)的最小生成树。
Prim算法:通过直接比较数组获取代价较小的边,需要的复杂度为O(n^2),适合稠密图
**初始状态:U ={u0},TE= {}。其中u0是顶点集合V中的某一个顶点
**在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(u0,v0),将这条边加进集合TE中,同时将此边的另一顶点v0并入U。这一步骤的作用是在边集E里找一条两个顶点分别在集合U和V-U中且权值最小的边,把这条边添加到边集TE中,并把这条边上不在U中的那个顶点加入到
**如果U = V,则算法结束;否则重复步骤2
**算法结束时,TE中包含了G中的n-1条边。经过上述步骤选取到的所有边恰好就构成了图G的一棵最小生成树
Kruskal算法:O(eloge),只与边数有关,适合于构造稀疏图
n首先将G中的n个顶点看成是独立的n个连通分量,这时的状态是有n个顶点而无边的森林,可以记为T= <V,{}>。
n
然后在E中选择代价最小的边,如果该边依附于两个不同的连通分支,那么将这条边加入到T中,否则舍去这条边而选择下一条代价最小的边。
n依此类推,直到T中所有顶点都在同一个连通分量中为止,此时就得到图G的一棵最小生成树。
void Kruskal(Graph& G, Edge*&MST) { // 数组MST用于保存最小生成树的边
ParTree<int>A(G.VerticesNum()); // 等价类
MinHeap<Edge>H(G.EdgesNum()); // 最小堆
MST= new Edge[G.VerticesNum()-1]; // 为数组MST申请空间
int MSTtag = 0; // 最小生成树的边计数
bool heapEmpty;
for (int i = 0; i < G.VerticesNum(); i++) // 将图的所有边插入最小堆H中
for(Edge e = G. FirstEdge(i);G.IsEdge(e);e= G. NextEdge(e))
if(G.FromVertex(e)< G.ToVertex(e)) // 对于无向图,防止重复插入边
H.Insert(e);
int EquNum= G.VerticesNum(); // 开始n个顶点分别作为一个等价类
while(EquNum> 1) { // 当等价类的个数大于1时合并等价类
heapEmpty= H.isEmpty();
if(!heapEmpty)
Edgee = H.RemoveMin(); // 获得一条权最小的边
if(heapEmpty|| e.weight== INFINITY) {
cout<< “不存在最小生成树.”<<endl;
delete[] MST; // 释放空间
MST= NULL; // MST赋为空
return;
}
int from = G.FromVertex(e); //记录该条边的信息
int to = G.ToVertex(e);
if(A.Different(from,to)){ // 边e的两个顶点不在一个等价类
A.Union(from,to); // 将边e的两个顶点所在的等价类合并为一个
AddEdgetoMST(e,MST,MSTtag++);// 将边e加到MST
EquNum–; // 等价类的个数减1
}
}
}