图
一、图的基本概念和相关性质
1、图的定义
图是由顶点和边集组成的集合,其中顶点的集合记作V,边集记作E,图记作G=(V,E)。
图不可以为空。
2、图相关概念
有向图:E是有向边的有限集合时,G就是有向图,对应的若弧是从v->w则记为<v,w>
v为弧头,w为弧尾。
无向图:E是无向边的有限集合时,G就是无向图。对应的弧若是v和w之间的弧则记为(v,w)。
简单图:满足不存在重复边;不存在顶点到自身的边的图为简单图。
多重图:图中两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联的图为多重图。
完全图:任意两个顶点之间都直接通路(不经过其他顶点)的图。完全无向图的边数为n(n-1)/2。完全有向图的边数为n(n-1)。
子图:有两个图G=(V,E)和g=(v,e);若v是V的子集,e是E的子集则g是G的子图。
连通:无向图中,若两顶点之间存在路径则两顶点时连通的。
连通图:图中任意两个顶点都是连通的图。
强连通:有向图中,若两顶点之间无论从哪一段到哪一段都有路径则两顶点是强连通的。
强连通子图:有向图中,任意两个顶点都是强连通的则称为强连通子图。
顶点的度:以该顶点为一个端点的边的数目,若无向图的边有e条则度为2e。若有向图的边有e条则度为e。
出度以该顶点为起点的有向边的数目。
入度以该顶点为终点的有向边的数目。
边的权:在图中,可以为每个边标上一定意义的数值,该数值为边的权值。
网:边上带有权值的图为网。
稠密图:边数相对于顶点很少的图。
稀疏图:边数相对于顶点很多的图。一般认为G中|E|<|V|×log|V|的图为稀疏图。
路径长度:从一个顶点到另一个顶点的路径上的边的数目。
回路:也称为环,第一个顶点和最后一个顶点相同的路径。当图有n个顶点时,若边数大于n-1,则一定有环。
简单路径:路径中,顶点不出现重复的路径。
简单回路:回路中,除第一个和最后一个顶点外,其余的顶点不重复的路径。
距离:若两顶点之间的最短路径存在则该路径的长度称为两顶点的距离。若不存在为无穷。
有向树:一个顶点的入度为0,其余顶点的入度为1的有向图为有向树。
3、图的基本操作
1) 图的基本操作
- CreateGraph(&G, V, E) 创建图
- DestroyGraph(&G) 销毁图
- LocateVex(G, u) 在图中定位u的位置
- GetVex(G, v) 返回图中顶点v的数据
- PutVex(&G, v, value) 设置图中顶点v的数据
- FirstAdjVex(G, v) 返回图中v的第一个邻接点
- NextAdjVex(G, v, w) 返回图中v的下一个邻接点(w是v的邻接点)
- InsertVex(&G,v) 在图中插入顶点v
- DleteVex(&G, v) 从图中删除顶点v
- InsertArc(&G, v, w) 在图中v和w之间添加一条弧
- DeleteArc(&G, v, w) 删除顶点v和w之间的弧
- DFSTraverse(G, visit) 深度优先搜索
- BFSTraverse(G, visit) 深度优先搜索
二、图的存储结构
1、邻接矩阵法
邻接矩阵的定义
邻接矩阵法就是用一个二维数组存储图中顶点与顶点之间的关系。
若为无权图,用1表示两个顶点之间有边,0表示无边;对于有权图用具体的数值表示顶点之间的权值,0或者无穷表示无边。
邻接表的结构定义
#define MAX_VERTEX_NUM 100
typedef int ElemType; //顶点的数据类型
typedef int EdageType; //带权图边上的数据类型
typedef struct
{
ElemType Vex[MAX_VERTEX_NUM]; //顶点表
EdageType edage[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //邻接表
int vexnum, arcnum; //顶点数和弧数
}MGraph;
缺陷:当图是稀疏图时会很浪费内存空间。
2、邻接表法
邻接表的定义
为每一个顶点建立一个链表该链表包含该顶点 的所有邻接顶点。
邻接表的结构定义
#define MAX_VERTEX_NUM 100 //图中顶点最大数目
typedef struct ArcNode //边表结点
{
int adjvex; //该弧指向的结点位置
struct ArcNode* next; //指向下一条弧的指针
//InfoType info; //弧的权值
}ArcNode;
typedef struct VNode //顶点
{
VertexType data; //顶点信息
ArcNode* first; //指向第一条依附于该顶点的弧的指针
}VNode, AdjList[MAX_VERTEX_NUM];
typedef struct
{
AdjList vertices; //邻接表
int vexnum, arcnum; //顶点数和弧数
}ALGraph;
3、十字链表
十字链表的定义
十字链表是有向图的一种存储结构。十字链表的结点定义如下:
其中tailvex指向弧尾,headvex指向弧头,链域hlink指向弧头相同的下一条弧,tlink指向弧尾相同的下一条弧info为该弧的相关信息;firstin指向以该顶点为弧头的第一个弧结点。firstout指向以该顶点为弧尾的第一个弧结点。
4、邻接多重表
邻接多重表的定义
邻接多重表是无向图的一种链式存储方式。
下面是其结构定义图:
mark用来标记该边是否被搜索过,ivex和jvex为该边依附的两个顶点的位置,ilink指向吓一跳依附于顶点ivex的边,jlink指向吓一跳依附于jvex的边,info为边的权值;data为顶点信息,firstedge指向第一条依附于该顶点的边。
邻接多重表的结构定义
#define MAX_VERTEX_NUM 100 //图中顶点最大数目
typedef struct ArcNode //边表结点
{
bool mark; //标记
int ivex, jvex; //分别指向该弧的两个顶点
struct ArcNode *ilink, *jlink; //分别指向两个顶点的下一条边
//InfoType info; //权值
}ArcNode;
typedef struct VNode //顶点
{
VertexType data; //顶点信息
ArcNode* firstedge; //指向第一条依附于该顶点的弧的指针
}VNode;
typedef struct
{
VNode adjmulist[MAX_VERTEX_NUM]; //多重邻接表
int vexnum, arcnum; //顶点数和弧数
}ALGraph;
三、图的遍历
参考图
1、广度优先搜索(BFS)
广度优先搜索是一种分层查找过程,就是先访问图的一个顶点,在访问该顶点所邻接的所有顶点,再访问与该顶点隔一个顶点的所有顶点依次类推。
实现代码
//BFS
bool visited[MAX_VERTEX_NUM]; //遍历标记数组
void BFSTraverse(Graph G, visit func)
{
int i = 0;
for(i = 0; i < G.vexnum; i ++)
{
visited[i] = FALSE; //初始化遍历标记数组
}
for(i = 0; i < G.vexnum; i ++)
{
if(!visited[i]) //对每个连通分量进行一次BFS
{
BFS(G, i, func);
}
}
}
void BFS(Graph G, int v, visit func)
{
sqQuqeue Q;
InitQueue(&Q); //初始化辅助队列
func(G, v); //访问图中第v个顶点
visited[v] = TRUE; //已经访问过的进行标记
EnQueue(&Q, v);
while(!isEmpty(Q))
{
DeQueue(&Q, &v);
for(w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor( G, v, w))
{
if(!visited[w])
{
visit(G, w);
visited[w] = TRUE;
EnQueue(&Q, w);
}
}
}
}
上面的图从顶点1开始进行广度优先搜索的结果为1 3 4 2 5;另外BFS的结果并不唯一结果和你选择的第一个顶点有关,也和寻找邻接点的方式有关。
2、深度优先搜索(DFS)
深度优先搜索和树的先序遍历相似,尽可能的深的搜索图中的顶点。当访问一个顶点结束时就访问其未被访问的邻接点直到无法再访问就一层一层向上返回重复该动作。
bool visited[MAX_VERTEX_NUM];
void DFSTraverse(Graph G, visit func)
{
int i = 0;
for(i = 0; i < G.vexnum; i ++) //初始化遍历标记数组
{
visited[v] = FALSE;
}
for(i = 0; G.vexnum; i ++) //遍历所有连通分量
{
if(!visited[v])
{
DFS(G, v, func);
}
}
}
void DFS(Graph G, int v, visit func)
{
int w = 0;
func(G, v); //访问图中第v个顶点
visited[v] = TRUE;
for(w = FirstNeighbor(G, v), w >= 0; w = NextNeighbor(G, v, w))
{
if(!visited[w])
{
DFS(G, w);
}
}
}