认识图
图的各种基本术语
图(Graph),又称为网络 (Network),由一个节点集合和边集合构 成,记作 G = (V, E),其中 V 是顶点集,E 是边集
节点/结点(node),又称顶点(vetex) 边(edge),又称链接(link),联系(tie),弧(arc)
图的 ADT
- 数据对象:具有相同特性的数据元素的集合,称为顶点集V。
- 数据关系:R = {< v,w > |v,w ∈ V&&P(v,w)},< v,w >表示从v到w的一条弧,v为弧尾,w为弧头;
P(v, w)定义了弧< v, w >的意义或信息 }
基本操作:
Create_Graph(): 图的创建操作,生成一个没有顶点的空图G
GetVex(G, v) :求图中的顶点v的值
DFStraver(G,v):从v出发对图G深度优先遍历, 每个顶点访问且只访问一次
BFStraver(G,v):从v出发对图G广度优先遍历, 每个顶点访问且只访问一次
图的实现
图的存储结构
图存储时要考虑的问题
任意顶点之间可能存在联系,无法以数据元素在存储区中的物理位置来表示元素之间的关系。图中顶点的度不一样,有的可能相差很大,若按度数最大的顶点设计结构,则会浪费很多存储单元,反之按每个顶点自己的度设计不 同的结构,又会影响操作。
常见的图存储结构
- 邻接矩阵/数组表示法
- 邻接链表
- 十字链表
- 邻接多重表
- 边表
邻接矩阵/数组表示法
顶点表 + 邻接矩阵包括:一个记录各个顶点信息的顶点表 + 一个表示各个顶点之间关系的邻接矩阵。或者更准确地说是“数组表示法”,一个一维顶点数组 + 一个二维边数组。
有向图和无向图的不同 1:对称性
- 无向图的邻接矩阵是对称的; 有向图的邻接矩阵可能是不对称的。
有向图和无向图的不同 2:求度的差异
- 在有向图中, 统计第 i 行 1 的个数可得顶点 i 的出度,统计第 i 列 1的个数可得顶点 i 的入度。
- 在无向图中, 统计第 i 行 (列) 1 的个数可得顶点 i 的度。
邻接矩阵的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #define INFINITY INT_MAX #define MAX_VERTEX_NUM 20 typedef enum{DG,DN,UDG,UDN} GraphKind;
typedef struct ArcCell{ WType w; InfoType *info; }ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct { VexType vexs[MAX_VERTEX_NUM]; AdjMatrix adj; int vexnum,arcnum; GraphKind kind; } MGraph;
|
图的构造
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Status CreateUDN(Mgraph &G){ scanf(&G.vexnum,&G.arcnum,&IncInfo); for(i=0;i<G.vexnum;++i) scanf(&G.vexs[i]); for (i=0 ;i<G.vexnum;++i) for (j=0;j<G.vexnum;++j) G.adj[i][j]={INFINITY,NULL}; for(k=0 ;k<G.arcnum;++k){ scanf(&v1,&v2,&w); i=LocateVex(G,v1); j=LocateVex(G,v2); G.adj[i][j].w=w; if (IncInfo) Input(*G.adj[i][j].info); G.adj[j][i]=G.adj[i][j]; } return OK; }
|
图的顶点定位
- 确定一个顶点在 vexs 数组中的位置 (下标) ,其过程完全等同于在顺序存储的线性表中查找一个数据元素
1 2 3 4 5 6 7
| int LocateVex(MGraph *G , VexType *vp){ intk; for (k=0;k< G->vexnum; k++) if (G->vexs[k]==*vp) return(k); return(-1); }
|
向图中增加一个顶点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int AddVertex(ALGraph *G ,VexType *vp){ int k, j; if (G->vexnum>=MAX_VEX){ printf(“Vertex Overflow !\n”); return(-1); } if(LocateVex(G , vp)!=-1){ printf(“Vertex has existed !\n”); return(-1); } G->vertices[G->vexnum].data=*vp; G->vertices[G->vexnum].degree=0; G->vertices[G->vexnum].firstarc=NULL; k=++G->vexnum; return(k); }
|
向图中增加一条弧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| int AddArc(ALGraph *G , ArcType *arc){ int k,j; ArcNode*p,*q; k=LocateVex(G , &arc->vex1) ; j=LocateVex(G , &arc->vex2) ; if (k==-1||j==-1) { printf(``弧依附的顶点不存在\n''); return(-1) ; } p=(ArcNode *)malloc(sizeof(ArcNode)) ; p->adjvex=arc->vex1 ; p->info=arc->info ; p->nextarc=NULL ; q=(ArcNode *)malloc(sizeof(ArcNode)) ; q->adjvex=arc->vex2 ; q->info=arc->info ; q->nextarc=NULL ; if (G->kind==UDG||G->kind==UDN) { q->nextarc=G->vertices[k].firstarc ; G->vertices[k].firstarc=q ; p->nextarc=G->vertices[j].firstarc ; G->vertices[j].firstarc=p ; } else{ q->nextarc=G->vertices[k].firstarc ; G->vertices[k].firstarc=q ; return(1); }
|
有向图的十字链表
有向图的链式存储结构:融合(拼接)邻接表和逆邻接表
有向图十字链表的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #define INFINITY MAX_VAL #define MAX_VEX 30 typedef struct ArcNode{ int tailvex, headvex; InfoType *info; struct ArcNode *hlink, *tlink; }ArcNode;
typedef struct VexNode{ VexType data; ArcNode *firstin, *firstout; }VexNode;
typedef struct{ int vexnum; VexNode xlist[MAX_VEX]; }OLGraph;
|
图解十字链表

图:十字链表
十字链表就是把邻接表和逆邻接表融合起来。(然而用的很少)
无向图的邻接多重表
仿造有向图的十字链表 (融合邻接表和逆邻接表);将无向图的邻接 表中的重复边节点去掉
节点结构如下图所示,说明如下:
- Data 域:存储和顶点相关的信息;
- 指针域 firstedge:指向依附于该顶点的第一条边所对应的表结点;
- 标志域 mark:用以标识该条边是否被访问过;
- ivex 和 jvex 域:分别保存该边所依附的两个顶点在图中的位置;
- info 域:保存该边的相关信息;
- 指针域 ilink:指向下一条依附于顶点 ivex 的边;
- 指针域 jlink:指向下一条依附于顶点 jvex 的边;

无向图的邻接多重表的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #define INFINITY MAX_VAL #define MAX_VEX 30 typedef emnu {unvisited, visited} Visitting; typedef struct EdgeNode{ Visitting mark; int ivex, jvex; InfoType *info; struct EdgeNode *ilink, *jlink; }EdgeNode;
typedef struct VexNode{ VexType data; ArcNode *firstedge; }VexNode;
typedef struct{ int vexnum; VexNode mullist[MAX_VEX]; }AMGraph;
|
图解邻接多重表

图:邻接多重表