大话数据结构学习笔记 - 图
图的定义
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:
G(V,E)
G
(
V
,
E
)
, 其中G
表示一个图, V
是图G
中顶点的集合,E
是图G
中边的集合
- 数据元素在线性表中被称为元素,在树中被称为结点,而在图中被称为顶点(
Vertext
) - 顶点集合
V
有穷非空 - 图中顶点之间的逻辑关系用边来表示,边集可以为空
各种图定义
- 无向边:若顶点
vi
v
i
到
vj
v
j
之间的边没有方向,则称这条边为无向边(
Edge
), 用无序偶对( vi,vj v i , v j )来表示 - 无向图:若图中任意两个顶点之间的边都是无向边,则为无向图(
Undirected graphs
) - 有向边:若从顶点
vi
v
i
到
vj
v
j
之间的边有方向,则称这条边为有向边,也成为弧(
Arc
),用有序偶 <vi,vj> < v i , v j > 表示, vi v i 称为弧尾, vj v j 称为弧头 - 有向边:如果图中任意两个顶点之间的边都是有向边,则称该图为有向图(
Directed graphs
) - 无向边用小括号
()
表示,有向边用尖括号<>
- 简单图:不存在顶点到其自身的边,且同一条边不重复出现,这样的图为简单图
- 无向完全图:任意两个顶点都存在边的无向图。 含有
n
个顶点的无向完全图有 n(n−1)2 n ( n − 1 ) 2 条边 - 有向完全图:任意两个顶点之间存在互为相反的两条弧的有向图。含有
n
个顶点的有向完全图有 n(n−1) n ( n − 1 ) 条边 - 稀疏图:有很少条边或弧的图称为稀疏图,反之为稠密图
- 权(
Weight
):与图的边或弧相关的数叫做权 - 网(
Network
):带权的图统称为网 - 子图(
Subgraph
):假设有两个图 G=(V,{E}) G = ( V , { E } ) 和 G′=(V′,{E′}) G ′ = ( V ′ , { E ′ } ) , 如果 V′⊆V V ′ ⊆ V 且 E′⊆E E ′ ⊆ E , 则称 G′ G ′ 为 G G 的子图
图的顶点与边间关系
- 无向图的邻接点(
Adjacent
):对于无向图, 如果边 (v,v′)∈E ( v , v ′ ) ∈ E , 则称顶点 v v 和互为邻接点。即 v v 和相邻接。边 (v,v′) ( v , v ′ ) 依附于顶点 v v 和,或者说 (v,v′) ( v , v ′ ) 与顶点 v v 和相关联 无向图的度(
Degree
):顶点v
的度是和v
相关联的边的数目, 记为 TD(v) T D ( v )有向图的邻接: 对于有向图 G=(V,{E}) G = ( V , { E } ) ,如果弧 <v,v′>∈E < v , v ′ >∈ E , 则称顶点
v
邻接到顶点 v′ v ′ , 顶点 v′ v ′ 邻接自顶点 v v 。弧 和顶点 v,v′ v , v ′ 相关联.- 有向图的入度(
InDegree
):以顶点v
为头的弧的数目称为v
的入度, 记为 ID(v) I D ( v ) - 有向图的出度(
OutDegree
):以顶点v
为尾的弧的数目称为v
的出度 , 记为 OD(v) O D ( v ) - 有向图的度: 顶点
v
的度为 TD(v)=ID(v)+OD(v) T D ( v ) = I D ( v ) + O D ( v ) - 回路或环(
Cycle
):第一个顶点到最后一个顶点相同的路径称为回路或环 - 简单路径:序列中顶点不重复出现的路径称为简单路径
- 简单回路或简单环:除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路,称为简单回路或简单环
连通图相关术语
- 连通图(
Connected Graph
):在无向图 G G 中,如果从顶点到顶点 v′ v ′ 有路径,则称 v v 和是连通的。如果对于图中任意两个顶点 vi,vj∈E v i , v j ∈ E , vi v i 和 vj v j 都是连通的, 则称 G G 是连通图 - 连通分量:无向图中的极大连通子图称为连通分量
- 强连通图: 在有向图中,如果对于每一对 vi,vj∈V,vi≠vj v i , v j ∈ V , v i ≠ v j , 从 vi v i 到 vj v j 和从 vj v j 到 vi v i 都存在路径,则称 G G 是强连通图。
- 强连通分量:有向图中的极大强连通子图称作有向图的强连通分量
- 生成树:一个连通图的生成树是一个极小的连通子图,它含有图中全部的
n
个顶点,但只有足以构成一棵树的n - 1
条边 - 有向树:如果一个有向图恰有一个顶点的入度为
0
, 其余顶点的入度均为1
, 则是一棵有向树
图的抽象类型
ADT 图(Graph)
Data
顶点的有穷非空集合和边的集合
Operation
CreateGraph(*G, V, VR):按照顶点集 V 和边弧集 VR 的定义构造图 G
DestroyGraph(*G):图 G 存在则销毁
LocateVex(G, u):若图 G 中存在顶点 u, 则返回图中的位置
GetVex(G, v):返回图 G 中顶点 v 的值
PutVex(G, v, value):将图 G 中顶点 v 赋值 value
FirstAdjVex(G, *v):返回顶点 v 的一个邻接顶点, 若顶点在 G 中无邻接顶点返回空
NextAdjVex(G, v, *w):返回顶点 v 相对于顶点 w 的下一个邻接顶点,若 w 是 v 的最后一个邻接点则返 回空
InsertVex(*G, v):在图 G 中增添新节点 v
DeleteVex(*G, v):删除图 G 中顶点 v 及其相关的弧
InsertArc(*G, v, w):在图 G 中增点弧 <v, w>, 若 G 是无向图,还需要增添对称弧 <w, v>。
DeleteArc(*G, v, w):在图 G 中删除弧 <v, w>,若 G 是无向图,则还需要删除对称弧 <w, v>。
DFSTraverse(G):对图 G 中进行深度优先遍历,在遍历过程对每个顶点调用
HFSTraverse(G):对图 G 中进行广度优先遍历,在遍历过程对每个顶点调用
endADT
图的存储结构
对于图的存储结构,简单的顺序存储结构以及多重链表都无法使用,最常用的是 邻接矩阵 和 邻接表
邻接矩阵
图的邻接矩阵(Adjacency Matrix
)存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息
设图中G
有n
个顶点,则邻接矩阵是一个的方阵,定义为
无向图邻接矩阵示意图
有向图邻接矩阵示意图
邻接矩阵的缺点是对于边数相对顶点较少的图,比较浪费存储空间
图的邻接矩阵存储结构
typedef char VertexType; /* 顶点类型 */ typedef int EdgeType; /* 边上的权值类型 */ #define MAXVEX 100 /* 最大顶点数 */ #define INFINITY 65535 /* 65535 代表无限大 */ typedef struct { VertexType vexs[MAXVEX]; /* 顶点表 */ EdgeType arc[MAXVEX][MAXVEX]; /* 邻接矩阵, 可看做边表 */ int numVertexes, numEdges; /* 图中当前的顶点数和边数 */ }MGraph;
无向网图的邻接矩阵表示建立
/* 无向网图的邻接矩阵表示 */ void CreateMGraph(MGraph *G) { int i, j, k, w; printf("输入顶点数和边数:\n"); scanf("%d,%d", &G->numVertexes, &G->numEdges); /* 输入顶点数和边数 */ for(i = 0; i < G->numVertexes; i++) /* 输入顶点信息,建立顶点表 */ scanf("%d", &G->vecs[i]); for(i = 0; i < G->numVertexes; i++) for(j = 0; j < G->numVertexes; j++) G->arc[i][j] = INFINITY; /* 邻接矩阵初始化 */ for(k = 0; k < G->numEdges; k++) /* 读入 numEdges 条边,建立邻接矩阵 */ { printf("输入边(vi, vj)上的下标i, 下标j 和权 w:\n"); scanf("%d,%d,%d", &i, &j, &w); /* 输入变(vi, vj)上的权 w */ G->arc[i][j] = w; G->arc[j][i] = G->arc[i][j]; /* 因为是无向图,矩阵对称 */ } }
链接表
邻接表(Adjacencty List
)是图的一种链式存储,并且将数组与链表相结合的表示方法。即图中顶点用一维数组存储,每个顶点的所有邻接点用链表存储
无向图的邻接表结构示意图
- 方便查找某个顶点的度,只需要查找顶点的边表中结点的个数
- 判断两定点是否存在边,只需要测试一顶点的边表中是否存在另一节点的下标即可
- 求某顶点的所有邻接点,只需要遍历边表即可
有向图的邻接表结构示意图
- 方便得到每个顶点的出度,若想得到入度,可建立逆邻接表。
图的邻接表存储结构
typedef char VertexType; /* 顶点类型 */ typedef int EdgeType; /* 边上的权值类型 */ typedef struct EdgeNode { int adjtex; /* 邻接点域,存储该顶点对应的下标 */ EdgeType weight; /* 用于存储权值,对于非网图可以不需要 */ struct EdgeNode *next; /* 链域,指向下一个邻接点 */ }EdgeNode; typedef struct VertexNode { VertexType data; EdgeNode *firstedge; }VertexNode, AdjList[MAXTEX]; typedef struct { AdjList adjList; int numVertexes, numEdges; }GraphAdjList;
建立图的邻接表结构
/* 建立图的邻接表结构 */ void CreateALGraph(GraphAdjList *G) { int i, j, k; EdgeNode *e; printf("输入顶点数和边数:\n"); scanf("%d,%d", &G->numVertexes, &G->numEdges); /* 输入顶点数和边数 */ for(i = 0; i < G->numVertexes; i++) /* 输入顶点信息,建立顶点表 */ { scanf(&G->adjList[i].data); /* 输入顶点信息 */ G->adjList[i].firstedge = NULL; /* 将边表置为空表 */ } for(k = 0; k < G->numEdges; k++) /* 建立边表 */ { printf("输入边(vi,vj)上的顶点序号:\n"); scanf("%d,%d", &i, &j); /* 输入边(vi,vj)上的顶点序号 */ e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向内存申请空间,生成边表结点 */ e->adjtex = j; /* 邻接序号为 j */ e.next = G->adjList[i].firstedge; /* 将 e 指针指向当前顶点指向的结点 */ G->adjList[i].firstedge = e; /* 将当前顶点的指针指向 e */ e = (EdgeNode *)malloc(sizeof(EdgeNode)); * 向内存申请空间,生成边表结点 */ e->adjnext = i; /* 邻接序号为 i */ e->next = G->adjList[j].firstedge; /* 将 e 指针指向当前顶点指向的结点 */ G->adjList[j].firstedge = e; /* 将当前顶点的指针指向 e */ } }
结语
后续会继续整理图的相关知识,本文章的代码code