为了巩固知识,做的笔记,还没有完善,如有错误请指正
一.图的概念
1.图G的定义:
(1)图由两个集合V,E组成,记为G=(V,E),V是有穷非空的顶点集,E是偶对顶点之间的有穷边集。
(2)通常情况下V(G)表示图G的顶点集合,E(G)表示图G的边集合,E(G)可以为空。(只有一个点也是图)
2.有向图和无向图
1.有向图<>
有向图中顶点对是有序的,<x,y>与<y,x>是不同的两条边。对于<x,y>称为从顶点x到顶点y的有向边,x是有向边的始点,y是有向边的终点。<x,y>也可以称为弧,x为弧尾,y为弧头。
2.无向图()
无向图中顶点对(x,y)是无序对,它称之为顶点x与顶点y相关联的一条边。(x,y)和(y,x)是同一条边。
二.图的基本术语
1.子图
G1的边集和点集,都是G2图的子集时,我们称G1为G2的子图。如图T1,T2,T3,T4都是G的子图。
2.完全图
(1)无向完全图
有n个节点,且边数为n(n-1)/2条边的无向图。
(2)有向完全图
有n个节点,且边数为n(n-1)条边的有向图。
3.稀疏图和稠密图
顶点之间边数很少的图称为稀疏图,反之称为稠密图。
4.邻接点
对于无向图(有向图也存在),若(v1,v2)属于其边集E,那么称v1,v2互为邻接点。即无向图有直接连线连线的两个点。
5.顶点v的度,入度,出度
(1)度(无向图):与点v相关联的边的条数。记为TD(v)。
(2)入度(有向图):指向v顶点的弧数。记为ID(v)。
(3)出度(有向图):从该顶点指向其邻接点的弧数。OD(v)。
对于有向图:TD(v)=ID(v)+OD(v)
对于无向图其边数等于度数的一半,即:e=(TD(V1)+TD(V2)+...+TD(Vn))/2
6.路径和路径长度
7.权和网
(1)权:每条边上具有某种实际意义的数值,例如时间,路程,花费。
(2)网:带权图称为网。
8.回路或环
9.简单路径、简单回路或环
10.连通、连通图和联通分量(连通图一定是无向图)
(1)连通: 在无向图G中,若点v1和v2之间有路径则称v1,v2是连通的。
(2)连通图: 图中任意两个顶点(vi到vj,vj到vi)都是连通的(都有路径),则该图称为连通图。
(3)连通分量: 若无向图为非连通图,则无向图中的极大连通子图称为连通分量,极大连通子图不唯一。
极大连通子图:连通的子图,但不存在一条e(e属于E),使其仍然连通。
11.强连通图和强连通分量(强连通和弱连通的概念只在有向图中存在)
(1)强连通图:在有向图G中,每一对vi,vj(vi,vj属于V,vi!=vj),从vi到vj和vj到vi都有路径,则称G是强连通图。
(2)强连通分量:有向图中的极大强连通子图称为强连通分量。与强连通图不同,极大强连通子图允许vi=vj,单一顶点v也是极大强连通分量。
注:不是强连通图也可以有强连通分量,极大强连通子图也是强连通
12.连通图的生成树
含有图中全部顶点,但只有足以构成一棵树的n-1条边的图。
注:强连通图的生成树也是极小连通图(再少一条边就必定不连通的子图)
13.有向树和生成森林
(1)有向树:有一个顶点入度为0,其余顶点的入度均为1的有向图称为有向树。
(2)生成森林:由若干个有向树组成,含有有向图中所有节点,但只有足以构成各自树且间不相交的弧。
二.案例引入
六度空间
三.图的存储结构
1.邻接矩阵
(1)邻接矩阵表示法:
a.图的邻接矩阵:表示顶点之间关系的矩阵,若两点之间存在边或弧,则两点下标对应的矩阵值为1,否则为0。
b.网(带权图)的邻接矩阵:表示顶点之间关系的矩阵,若两点之间存在边或弧,则两点下标对应的矩阵值为权值,否则为无穷(远远大于任意权值的数)。
(2)图的邻接矩阵存储结构
- 一个二维数组存放矩阵的值。
- 一个一维数组存储顶点的信息(v1,v2...)。
//图的邻接矩阵存储表示
#define MaxInt 74776 //表示无穷
#define MVNum 100 //最大顶点数
typedef char VerTexType; //将顶点的数据类型设为char且名字为VerTexType
typedef int ArcType; //将权值的数据类型设为int且名字为ArcType
typedef struct{
VerTexType vexs[MVNum]; //顶点表
ArcType arcs[MVNum][MAVnum]; //邻接矩阵
int vexnum,arcnum; //存储图的顶点数和边数
}AMGraph;
(3)邻接矩阵创建无向网
- 输入顶点数和边数
- 将各顶点的信息(v1,v2...)录入顶点表
- 初始化邻接矩阵。将矩阵值全部置为MaxInt
- 输入想要录入权值的边及其依附点(依附点的值就是邻接矩阵的数组下标)
- 根据数组下标对应的边,将权值录入
int CreateUDN(AMGraph &G){
cin>>G.vexnum>>G.arcnum; //输入顶点数和边数
for(int i=0;i<G.vexnum;i++)
cin>>vexs[i]; //录入顶点值信息
for(int i=0;i<G.vexnum;i++){
for(int j=0;i<G.vexnum;j++){
arcs[i][j]=MaxInt; //初始化邻接矩阵,边的权值全部设为MaxInt;
}
}
for(int k=0;i<G.arcnum;k++){
cin>>v1>>v2>>w; //输入边的依附点和边的权值
i=LocateVex(G,v1);j=LocateVex(G,v2);//确定V1,V2的位置,即数组的下标
arcs[i][j]=w; //边<v1,v2>的权值置为w
arcs[j][i]=arcs[i][j];//<v1,v2>,<v2,v1>的权值都为W
}
return 1;
}
(4)邻接矩阵表示法的优缺点
优点
- 可以根据A[i][j]=0或1判断两个顶点间是否有边。
- 便于判断各个点的度数,对于无向图,矩阵第i行元素之和就是顶点Vi的度数。对于有向图,矩阵第i行元素之和为出度,第i列元素之和就是入度。
缺点 - 不便于增加删除节点
- 不便于统计边的数目,需要遍历矩阵所有元素。对于无向图遍历完所有元素得到图的总度数除以2得到边数,对于有向图遍历完得到的总度数就是边数。
- 空间复杂度为n的平方,浪费空间。
2.邻接表
(1)邻接表的表示法
邻接表由表头节点表和边链表组成,表头节点依次存储图中的所有顶点,边链表记录某一个顶点的所有关联点。(表的每一行记录每一个点的发散)
- 表头节点表:包括数据域和链域,数据域用来存放顶点的信息,链域用来指向第一个节点。为了可以随机访问任一顶点的边链表,表头节点以顺序形式存储 。另外为了方便获取每个边链表的节点个数,可以在头节点处再开辟一块数据域来存放边界点的数量。
- 边节点链表:由数据域(info)、链域(nextArc)、邻接点域(adjVex)组成,邻接点域用来存放与顶点邻接的点的位置(表头节点的索引),链域存放下一个邻接点的地址,数据域用来存放边的相关信息(权值)。边链表节点之间没什么关联,都只与表头节点关联。
(2)邻接表的存储结构
#define MVNum 100
typedef char VerTexType;
typedef int ArcType;
typedef int Otherinfo;
typedef struct ArcNode{ //存储边节点
int adjVex; //存储节点的序号
struct ArcNode *nextarc; //存储下一个节点的地址
Otherinfo info; //存储边的相关信息
}ArcNode;
typedef struct VNode{ /存储表头节点信息
VerTexType data; //存储表头节点的信息
ArcNode *firstarc; //表头节点的指针域
}VNode,AdjList[MVNum]; //AdjList可以使表头节点以数组的形式存储
typedef struct{ //存储图的信息
AdjList vertices; //定义了AdjList类型的数组存储结构,存储边节点的数据域和指针域信息
int vexnum,arcnum; //记录顶点数和边数
}ALGraph;
(3)邻接表表示法创建无向图
int CreateUDG(ALGgraph &G){
cin>>G.vexnum>>G.arcnum; //输入图的节点数和边数
for(int i=0;i<G.vexnum;++i){
cin>> G.vertices[i].data; //将顶点的信息录入到表头节点表中
G.vertices[i].firstarc=NULL; //初始化表头节点的指针域
}
for(int k=0;k<G.arcnum;++k){
cin>>v1>>v2; //输入边的两个依附点
i=LocateVex(G,v1); j=LocateVex(G,v2);
//找到v1,v2的序号
ArcNode p1=new ArcNode;
p1->adjvex=j;
p1->nextarc=G.vertices[i].firstarc; G.vertices[i].firstarc=p1;
//将边节点v2插入到表头节点v1的后面(v1,v2)
ArcNode p2=new ArcNode;
p2->adjvex=i;
p2->nextarc=G.vertices[j].firstarc; G.vertices[j].firstarc=p2;
//将边节点v1插入到表头节点v2的后面(v2,v1)
}
return 1;
}
(4)邻接表表示法的优缺点
优点:
- 便于增加删除节点。
- 便于统计边的条数:按顶点表顺序顺序查找所有边即可得到边的目数。(有向图直接遍历,无向图遍历完成后总数除以2)---时间复杂度为O(n+e)。
- 存储空间效率高,适合表示稀疏表。
缺点:
- 不便于直接查看两点vi和vj之间是否有边,若要查看必须遍历第i个边表,最坏情况下时间复杂度为O(n)。
- 不便于统计顶点的度数(求出度容易,入度困难):对于无向图,顶点vi的度是第i个边表中节点个数;对于有向图,顶点vi的出度为是第i个边表的节点个数,入度需要遍历各个顶点的边表,查看各个顶点的边表节点是否含有vi。
四.图的遍历
前言:
- 图的遍历算法是求解图的连通性问题、拓扑排序和关键路径等算法的基础。
- 由于图的任意节点都可能和其余的顶点连接,图的顶点可能会被访问多次,需要设置一个visited[]数组标记该顶点是否已经被访问--ture(1)已经被访问,false(0)未被访问。
1.深度优先搜索---树的先序遍历的推广
(1)遍历步骤
- a:从图中选一个节点作为顶点出发。
- b:访问该顶点第一个未被访问过的邻接点,访问该邻接点后,以该邻接点为新的顶点,继续访问该顶点第一个未被访问过的节点。
- c:重复步骤b,直至有一个顶点的邻接点全被访问过。
- d:返回该顶点前一个且仍有未被访问邻接点的顶点。
- e: 重复b,c,d直至所有节点都被访问过。
(2)算法实现
从图中选择一个顶点v出发,访问v,并设置visited[v]=ture(1)
深度优先遍历的抽象表示:
连通图
非连通图
深度优先遍历的具体表示:
邻接矩阵表示
邻接表表示
(3)算法分析---时间复杂度与查找的次数有关
- 邻接矩阵:邻接矩阵因为对每个顶点 v 都要通过遍历一次一维数组 matrix[v][ ]找到它的所有邻接点,对应的时间复杂度O ( n ),所以总的时间复杂度Ο(n^2)。
- 邻接表:邻接表因为本身存储的就是有相连关系的邻接点,所以查找所有顶点的邻接点的时间复杂度为O ( e )。又因为对每个顶点都要进行一次查找,所以总的时间复杂度O ( n + e )。
2.广度优先搜索---树的先序遍历的推广
(1)遍历步骤
- a.从图中的一个顶点 v出发并访问。
- b.访问顶点 v的所有邻接点。
- c.按照邻接点的访问顺序,访问以邻接点为顶点的所有未被访问的邻接点。
- d.重复步骤c,直到图中所有的顶点都被访问。
(2)算法实现
(3)算法分析---时间复杂度与查找的次数有关
- 邻接矩阵:邻接矩阵因为对每个顶点 v 都要通过遍历一次一维数组 matrix[v][ ]找到它的所有邻接点,对应的时间复杂度O ( n ),所以总的时间复杂度Ο(n^2)。
- 邻接表:邻接表因为本身存储的就是有相连关系的邻接点,所以查找所有顶点的邻接点的时间复杂度为O ( e )。又因为对每个顶点都要进行一次查找,所以总的时间复杂度O ( n + e )。
五.图的应用
1.最小生成树---最小代价生成树(没有回路)
最小生成树定义:在一个连通网的所有生成树中,各边代价之和最小的生成树。
(1)普利姆算法
- 算法思想:加点法
- 算法步骤:
a.从图中选一个节点出发。
b.连接与该节点有关联的权值最小的节点。
c.比较各个已连接的节点与未连接节点的权值,选择最小的连接(形成回路的边不连接)。
d.重复c步骤,直至有n-1条边被连接。(n为顶点个数) - 算法实现
(2)克鲁斯卡尔算法
- 算法思想:加边法
- 算法步骤:
从所有边中,选择未被选中的,权值最小边e,边e的关联点不能在同一个连通分量中(不能形成回路),直至有n-1条边被选中(n为顶点个数)。 - 算法实现
2.最短路径
(1)迪杰斯特拉算法---单源最短路径--->从一个顶点到其余各个顶点的最短路径
- 算法思想:中转节点法
- 算法步骤:
a.设置两个顶点的集合S和U,集合S中存放已找到最短路径的顶点(也是下一轮计算中可以经过的节点),集合U中存放当前还未找到最短路径的顶点。初始状态时,集合S中只包含源点,设为v0.
b.计算该轮源点v0到U中所有节点的路径长度(不能到达的记为MAXINT),将最短的路径的末节点放入到集合S中。
c.不断重复b步骤,直到集合U中的顶点全部加入到集合S中为止。 - 算法实现
例题
先计算第一个顶点v0到其他顶点的路径长度,将最短的路径长度的末节点v1放入S中;然后以末节点v1为新的节点,找该节点到其他节点的最短路径,将该路径的末节v2点放入S中。v0到v2的最短路径=v0到v1最短路径+v1到v2最短路径之和
(2)弗洛伊德算法(解封算法)---多源头最短路径--->图中每一对顶点之间的最短路径
- 算法思想:解封顶点法
- 算法步骤:
- 算法实现
3.拓扑排序
定义:拓扑排序就是将AOV-网中所有顶点排成一个线性序列,该序列满足:若在AOV-网中从顶点vi到顶点vj有一条
路径,则该线性序列中的顶点vi必定在顶点vj之前。
顶点活动网(AOV网) :将顶点表示活动,边表示活动之间的关系的网称为顶点活动网。
- 算法思想:0入删除法
- 算法步骤:
(1)在有向图中选一个入度为0的顶点输出。
(2)从图中删除该顶点及所有它的出边(多个顶点都可以删除时,一般先删除序号较小的)。
(3)重复执行1和2,直到全部顶点均已输出,或图中剩余顶点的入度均不为0(说明图中存在回路,无法继续拓扑排序)。 - 算法实现
4.关键路径
AOE网:带权的有向图,顶点表示事件,边表示活动,权表示活动持续的时间。
顶点vi表示事件
边<vi,vj>表示活动
边的活动完成后,相应的点的事件也完成,活动的发生是为了完成事件。
关键路径:在AOE网中,从始点到终点具有最大路径长度(该路径上的各个活动所持续的时间之和)的路径称为关键路径。
关键活动:关键路径上的活动称为关键活动。
事件的最早完成时间=源点到该点的最大路径长度(汇入该点的所有路径都完成事件才能发生)
事件的最迟完成时间=AOE网的关键路径-汇点到该点的最大路径长度(给最长路径留足够的时间完成)。
活动的最早发生时间=该活动的始点到源点的最大路径长度,活动的最早发生时间等于活动左端点的事件最早发生事件
活动的最迟发生时间=该活动终点事件的最迟发生时间-活动的持续时间(权值)
求关键活动:活动的最早发生时间与最晚发生时间相当的活动为关键活动。