一、图的抽象数据结构类型:
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;
}
......