一、图的抽象数据结构类型:
ADT Graph{ 数据对象V:V是具有相同特性的数据元素的集合,称为顶点集。 数据关系R:R={VR} VR={<v,w>|v,w∈V且P(v,w),<v,w>表示从v到w的弧, 谓词P(v,w)定义了弧<v,w>的意义或信息} 基本操作: CreateGraph( &G, V, VR ) 初始条件:V是图的顶点集,VR是图中弧的集合。 操作结果:按V和VR的定义构造图G。 DestroyGraph( &G ) 初始条件:图G存在。 操作结果:销毁图G。 LocateVex( G, u ) 初始条件:图G存在,u和G中顶点有相同特征。 操作结果:若G中存在顶点u,则返回该顶点在图中位置;否则返回其它信息。 GetVex( G, v ) 初始条件:图G存在,v是G中某个顶点。 操作结果:返回v的值。 PutVex( &G, v, value ) 初始条件:图G存在,v是G中某个顶点。 操作结果:对v赋值value。 FirstAdjVex( G, v ) 初始条件:图G存在,v是G中某个顶点。 操作结果:返回v的第一个邻接顶点。若顶点在G中没有邻接顶点,则返回“空”。 NextAdjVex( G, v, w ) 初始条件:图G存在,v是G中某个顶点,w是v的邻接顶点。 操作结果:返回v的(相对于w的)下一个邻接顶点。若w是v的最后一个邻接点,则返回“空”。 InsertVex( &G, v ) 初始条件:图G存在,v和图中顶点有相同特征。 操作结果:在图G中增添新顶点v。 DeleteVex( &G, v ) 初始条件:图G存在,v是G中某个顶点。 操作结果:删除G中顶点v及其相关的弧。 InsertArc( &G, v, w ) 初始条件:图G存在,v和w是G中两个顶点。 操作结果:在G中增添弧<v,w>,若G是无向的,则还增添对称弧<v,w>。 DeleteArc( &G, v, w ) 初始条件:图G存在,v和w是G中两个顶点。 操作结果:在G中删除弧<v,w>,若G是无向的,则还删除对称弧<v,w>。 DFSTraverse( G, Visit() ) 初始条件:图G存在,Visit是顶点的应用函数。 操作结果:对图进行深度优先遍历。在遍历过程中对每个顶点调用函数Visit一次且仅一次。一旦visit()失败,则操作失败。 BFSTraverse( G, Visit() ) 初始条件:图G存在,Visit是顶点的应用函数。 操作结果:对图进行广度优先遍历。在遍历过程中对每个顶点调用函数Visit一次且仅一次。一旦visit()失败,则操作失败。 }ADT Graph
二、图的存储结构:
1.数组表示法:
#define INFINITY INT_MAX //最大值 #define MAX_VERTEX_NUM 20 //最大顶点数 typedef enum {DG,DN,UDG,UDN} GraphKind; //DG表示有向图, DN表示有向网, UDG表示无向图, UDN表示无向网 typedef struct ArcNode { VRType adj; //VRType是顶点关系类型。对于无权图,用1或0表示相邻否;对带权图,则为权值类型。 char * info;//该弧相关信息的指针 }ArcNode, AdjMatrix[MAX_V_N][MAX_V_N];//邻接矩阵 typedef struct { VertexType vexs[MAX_VERTEX_NUM]; //顶点向量 AdjMatrix arcs; //邻接矩阵 int vexnum,arcnum; //图的当前顶点数和弧数 GraphKind kind; //图的种类标志 }MGraph;
Status CreateGraph( MGraph &G ) { // 采用数组(邻接矩阵)表示法,构造图G。 scanf(&G.kind); // 自定义输入函数,读入一个随机值 switch (G.kind) { case DG: return CreateDG(G); // 构造有向图G case DN: return CreateDN(G); // 构造有向网G case UDG: return CreateUDG(G); // 构造无向图G case UDN: return CreateUDN(G); // 构造无向网G default : return ERROR; } }
Status CreateUDN(MGraph &G) { // 采用数组(邻接矩阵)表示法,构造无向网G。 scanf("%d,%d,%d",&G.vexnum, &G.arcnum, &IncInfo); for (i=0; i<G.vexnum; i++ ) scanf("%c",&G.vexs[i]); // 构造顶点向量 for (i=0; i<G.vexnum; ++i ) // 初始化邻接矩阵 for (j=0; j<G.vexnum; ++j ) { G.arcs[i][j].adj = INFINITY; G.arcs[i][j].info= NULL; } for (k=0; k<G.arcnum; ++k ) { // 构造邻接矩阵 scanf(&v1,&v2,&w); // 输入一条边依附的顶点及权值 i = LocateVex(G, v1); j = LocateVex(G, v2); // 确定v1和v2在G中位置 G.arcs[i][j].adj = w; // 弧<v1,v2>的权值 if (IncInfo) scanf(G.arcs[i][j].info); // 输入弧含有相关信息 G.arcs[j][i].adj = G.arcs[i][j].adj; // 置<v1,v2>的对称弧<v2,v1> } return OK; }
二、邻接表:
1.结点的结构:
2.邻接表和逆邻接表:
(<有向图的>逆邻接表:对每个顶点vi建立一个以vi为头的链表,便于确定顶点的入读或以顶点vi为头的弧。)
3.邻接表的实现:
//表结点的结构 typedef struct ArcNode { int adjvex; //该弧所指向的顶点的位置 struct ArcNode *nextarc; //指向下一条弧的指针 InfoType *info; //该弧相关信息的指针(如权值) }ArcNode; //头结点结构 typedef struct VNode { VertexType data; //顶点信息 ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 }VNode,AdjList[MAX_VERTEX_NUM]; //图的结构 typedef struct { AdjList vexs; int vexnum,arcnum; //图的当前顶点数和弧数 int kind; //图的种类标志 }ALGraph;
//------------------图的构造函数------------------------ void CreateALGraph(ALGraph &G) { scanf("%d,%d,%d",&G.vexnum, &G.arcnum, &G.kind); for (i=0; i<G.vexnum; i++) { //初始化头结点 cin >> G.vexs[i].data; G.vexs[i].firstarc = NULL; } for (k=0; k<G.arcnum; k++) { scanf(&v1,&v2,&w); // 输入一条边依附的顶点及权值 i = LocateVex(G, v1); j = LocateVex(G, v2); p = (ArcNode*)malloc(sizeof(ArcNode)); p->adjvex = j; p->info = w; p->nextarc = G.vexs[i].firstarc; G.vexs[i].firstarc = p; if (G.kind==UDG) { //如果是无向图,执行此步骤 p=(ArcNode*)malloc(sizeof(ArcNode)); p->adjvex=i; p->info = w; p->nextarc=G.vexs[j].firstarc; G.vexs[j].firstarc=p; } } }
三、十字链表:
1.结点结构:
在弧结点中有五个域:其中尾域(tailvex)和头域(headvex)分别指示弧尾和弧头这两个顶点在图中的位置,链域hlink指向弧头相同的下一条弧,而链域tlink指向弧尾相同的下一条弧,info域指向该弧的相关信息。弧头相同的弧在同一链表上,弧尾相同的弧也在同一链表上。
头结点即为顶点结点,它由三个域组成:其中data域存储和顶点相关的信息,如顶点的名称等;firstin和firstout为两个链域,分别指向以该顶点为弧头或弧尾的第一个弧结点。
2.十字链表:
3.十字链表的实现:
Status CreateDG(OLGraph &G) { // 采用十字链表存储表示,构造有向图G(G.kind=DG)。 scanf(&G.vexnum, &G.arcnum, &IncInfo); // 自定义输入函数 for (i=0; i<G.vexnum; ++i) { // 构造表头向量 scanf(&G.xlist[i].data); // 输入顶点值 G.xlist[i].firstin = G.xlist[i].firstout = NULL; // 初始化指针 } for (k=0; k<G.arcnum; ++k) { // 输入各弧并构造十字链表 scanf(&v1, &v2); // 输入一条弧的始点和终点 i=LocateVex(G, v1); j=LocateVex(G, v2); // 确定v1和v2在G中位置 p=(ArcBox *) malloc (sizeof (ArcBox)); // 假定有足够空间 *p = {i, j, G.xlist[j].firstin, G.xlist[i].firstout, NULL} // {tailvex, headvex, hlink, tlink, info} G.xlist[j].firstin = G.xlist[i].firstout = p; // 完成在入弧和出弧链头的插入 if (IncInfo) Input(*p->info); // 输入弧含有相关信息 } return OK; }
......