数据结构之图
图的定义和术语
图:G=(V,E)
V:顶点的有穷非空集合;
E:边的有穷集合;
无向图:每条边都是无方向的
有向图:每条边都是有方向的
完全图:任意两个点都有一条边相连
稀疏图:有很少边或者弧的图
稠密图:有较多边或弧的图
网:边、弧带权的图
邻接:有边、弧相连的两个顶点之间的关系
存在(vi,vj),则称vi和vj互为邻接点
存在<vi,vj>,则称vi邻接到vj,vj邻接于vi
**关联(依附)**:边、弧与顶点之间的关系,存在(vi,vj)/<vi,vj>,则称该边/弧关联于vi和vj
顶点的度:与该顶点相关联的边的数目,记为TD(v)
在有向图中,顶点的度等于该顶点的入度与出度之和,顶点v的入度是以v为终点的有向边的条数,记为ID(v);顶点的出度是以v为始点的有向边的条数,记作OD(v)
![]()
路径:接续的边构成的顶点序列
路径长度:路径上边或弧的数目/权值之和
回路:第一个顶点和最后一个顶点相同的路径
简单路径:除路径起点和终点可以相同外,其余顶点均不相同
简单回路:除路径起点和终点相同外,其余顶点均不相同
连通图(强联通图)
任意两个顶点都存在路径![]()
权与网:图中边或弧所具有的相关数称为权,表明从一个顶点到顶一个顶点的距离或耗费
子图:
**连通分量(强连通分量)**:
无向图
有向图:
有向图的极大连通子图称为其强连通分量
极小连通子图:该子图是G的连通子图,在该子图中删除任意一条边会使子图不再连通
生成树:包含无向图G所有顶点的极小连通子图
生成森林:对非连通图,由各个连通分量的生成树的集合
图的存储结构
1.数组表示法(邻接矩阵)
2.链式存储结构:邻接表、邻接多重表、十字链表
数组表示法(邻接矩阵)
建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点之间的关系)
无向图的邻接矩阵表示法
有向图的邻接矩阵表示法
网(有权图)的邻接矩阵表示法
邻接矩阵的存储表示
用两个数组分别存储顶点表和邻接矩阵
#define MaxInt 32767 //表示极大值
#define MVNum 100 //最大顶点数
typedef char VerTexType;//设定点的数据类型为字符型
typedef int ArcType;//假设边的权值类型为整型
typedef struct {
VerTexType vexs[MVNum];//顶点表
ArcType arcs[MVNum][MVNum]; //邻接矩阵
int vexnum,arcnum;//图的当前点数和边数
}AMGraph;
算法1 采用邻接矩阵表示法创建无向图
Status CreateUND(AMGragh &G){
cin>>G.vexnum>>G.arcnum;//输入总顶点数、总边数
for(i=0;i<G.vexnum;++i){
cin>>G.vex[i];//依次输入点的信息
}
for(i=0;i<G.vexnum;++i)
for(j=0;j<G.vexnum;++j)
G.arcs[i][j]=Maxint;//边的权值均置为极大值
for(k=0;k<G.arcnum;++k){
//构造邻接矩阵
cin>>v1>>v2>>w;//输入一条边所依附的顶点及边的权值
i=LocateVex(G,v1);
j=LocateVex(G,v2);//确定v1和v2在G的位置
G.arcs[i][j]=w;
G.arcs[j][i]=G.arcs[i][j];//边<v1,v2>的权值设置为w,置对称边权值为w
}
return OK;
}
补充算法 在图中查找顶点
int LocateVex(AMGraph G,VertexType u){
//在图G查找顶点u,返回顶点下标
int i;
for(i=0;i<G.vexnum;++i){
if(u==G.vex[i])return i;
}
return -1;
}
邻接矩阵的优缺
优点:
![]()
缺点:
![]()
邻接表表示法(链式)
无向图:
有向图:
邻接表的存储表示
顶点的结点结构
typedef struct VNode{
VerTexType data;//顶点信息
ArcNode *firstarcs; //指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum];
例如AdjList v;相当于:VNode v[MVNum];
边结点结构
#define MVNum 100//最大顶点数
typedef struct ArcNode{
//边结点
int adjvex//该边所指向的顶点位置
struct ArcNode *nextarc;//指向下条边的指针
OtherInfo info; //和边相关的信息
}ArcNode;
图的结构定义
typedef struct {
AdjList vertices;//vertices--vertex的复数
int vexnum,arcnum;//图的当前顶点数和弧数
}ALGraph;
邻接表操作举例说明:
采用邻接表表示法创建无向图
Status CreateUDG(ALGraph &G) {
//采用邻接表表示法,创建无向图G
cin>>G.vexnum>>G.arcnum;
for(i=0;i<G.vexnum;++i){
cin>>G.vertices[i].data;
G.vertices[i].firstarc=NULL;
}
for(k=0;k<G.arcnum;++k){
cin>>v1>>v2;
i=LocateVex(G,v1);
j=LocateVex(G,v2);
p1=new ArcNode;
p1->adjvex=j;
p1->nexrarc=G.vertices[i].firstarc;
G.vertices[i].firstarc=p1;
p2=new ArcNode;
p2->adjvex=i;
p2->nexrarc=G.vertices[j].firstarc;
G.vertices[j].firstarc=p2;
}
return OK;
}
邻接表的优缺
邻接矩阵与邻接表的关系
可供选择的其他存储结构
十字链表
顶点结点:
firstin:第一条入弧
firstout:第一条出弧
弧结点:
tailvex:弧尾位置
headvex:弧头位置
hlink:弧头相同的下一条弧
tlink:弧尾相同的下一条弧
邻接多重表
图的遍历-深度优先
定义:从已给的连通图中某一顶点出发,沿着一些边访遍图中的所有顶点,且使每个顶点仅被访问一次
遍历实质:找到邻接点
图的特点:
图中可能存在回路,且图的任一顶点都可能与其他顶点相通,在访问完某个顶点之后可能会沿着某些边又回到曾经访问过的顶点,如何避免?
邻接矩阵表示的无向图深度遍历实现
void DFS(AMGraph G,int v){
//图G为邻接矩阵类型
cout<<v;
visited[v]=true;//访问第v个顶点
for(w=0;w<G.vexnum;w++)//依次检查邻接矩阵v所在的行
if((G.arcs[v][w]!=0)&&(!visited[w]))DFS(G,w);
//w是v的邻接点,如果w未访问,则递归调用DFS
}
深度遍历效率
非连通图的深度优先遍历
图的遍历-广度优先
邻接表表示的无向图广度遍历实现
利用队列实现
F:队头指针 R:队尾指针
void BFS(Graph G,int v){
//按广度优先非递归遍历图G
cout<<v;
visited[v]=true;//访问第v个顶点
InitQueue(Q);//辅助队列Q初始化,置空
EnQueue(Q,v);//v进队
while(!QueueEmpty(Q)){//队列非空
DeQueue(Q,u);//队头元素出队并置为u
for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))
if(!visited[w]){
//w为u的尚未访问的邻接顶点
cout<<w;visited[w]=true;
EnQueue(Q,w);//w进队
}
}
}
广度遍历与深度遍历的比较
最小生成树
回顾:生成树:所有顶点均由边连接在一起,但是不存在回路的图
·一个图可以有许多棵不同的生成树
·所有生成树具有以下共同特点:
1.生成树的顶点个数与图的顶点个数相同
2.生成树是图的极小连通子图
3.在生成树中再加一条边必然形成回路
4.生成树中任意两个顶点间的路径是唯一的
无向图的生成树
利用优先遍历即可
深度优先遍历形成的生成树
广度优先遍历形成的生成树
最小生成树的定义
给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那颗生成树称为最小生成树
构造最小生成树
MST性质(一种贪心算法):设N=(V,E)是一个连通网,U是顶点集V的一个非空子集。若边(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必存在一棵包含边(u,v)的最小生成树。
![]()
构造最小生成树方法1-Prime算法
算法思想: 不断扩大最小生成树的范围
构造最小生成树方法2-Kruskal算法
算法思想:直接选当前最小的边
注:最小生成树可能不唯一
两种算法的比较
最短路径
顶点:表示地点
弧:表示两地连通
弧上的权值:表示距离或花费等等
第一类问题:两点间的最短路径
所有顶点间的最短路径-弗洛伊德算法
第二类问题:某源点到其他各点最短路径
单源最短路径-迪杰斯特拉算法
单源最短路径-迪杰斯特拉算法
所有顶点间的最短路径-弗洛伊德算法
拓扑排序
有向无环图:
拓扑排序例子
AOV网的特点:
拓扑排序定义
拓扑排序方法
简言之:一直选择没有前驱的点
检测AOV网中是否存在环的方法
关键路径
例:
把工程计划表示为边表示活动的网络,即AOE网,用顶点表示事件,弧表示活动,弧的权表示活动持续的时间
关键路径:路径长度最长的路径
路径长度:路径上各活动持续时间之和
求关键路径步骤:
时间余量为0即为关键活动