第六章图(6.4)

6.4图的存储结构

6.4.1邻接矩阵

1.邻接矩阵表示法

邻接矩阵是表示顶点之间相邻关系的矩阵。

用邻接矩阵表示法表示图,除了一个用于存储邻接矩阵的二维数组外,还需要一个一维数组来存储顶点信息,形式说明如下:

/*图的邻接矩阵存储表示法*/
//用两个数组分别存储顶点表和邻接矩阵
#define MaxInt 32767  //表示极大值,即无穷
#define MVNum 100  //最大顶点数
typedef char VerTexType;  //假设顶点的数据类型为字符型
typedef int ArcType;	//假设边的权值类型为整型
typedef struct
{
	VerTexType vexs[MVNum];	//顶点表
	ArcType arcs[MVNum][MVNum];	//邻接矩阵
	int vexnum, arcnum;	//图的当前顶点数和边数
}AMGraph;

2.采用邻接矩阵表示法创建无向网

已知一个图的点和边, 使用邻接矩阵表示法来创建此图的方法比较简单, 下面以一个无向网为例来说明创建图的算法。

算法6.1 采用邻接矩阵表示法创建无向网

算法步骤
  1. 输入总顶点数和总边数。
  2. 依次输入点的信息存入顶点表中。
  3. 初始化邻接矩阵, 使每个权值初始化为极大值。
  4. 构造邻接矩阵。依次输入每条边依附的顶点和其权值,确定两个顶点在图中的位置之后,使相应边赋予相应的权值,同时使其对称边赋予相同的权值。
算法描述
Status CreateUDN(AMGraph &G) 
{//采用邻接矩阵表示法,创建无向网G
cin>>G.vexnum>>G.arcnum; //输人总顶点数,总边数
for(i=O;i<G.vexnum;++i) //依次输入点的信息
  cin>>G.vexs[i); 
for(i=O;i<G.vexnum;++i)//初始化邻接矩阵,边的权值均置为极大值Maxint
  for (j =0; j <G. vexnum; ++j) 
    G.arcs[i] [j]=Maxint;
for(k=O;k<G.arcnum;++k) //构造邻接矩阵
{
  cin>>vl>>v2>>w; //输人一条边依附的顶点及权值
  i=LocateVex(G,vl);j=LocateVex(G,v2); //确定vl和v2在G中的位置,即顶点数组的下标
  G.arcs[i] [j]=w; //边<vl, v2>的权值置为w
  G.arcs[j] [i]=G.arcs[i] [j];
  }
return OK; 
} 

3.邻接矩阵的优缺点

(1)优点

  1. 便于判断两个顶点之间是否有边, 即根据A[i] [j]= 0或1来判断。
  2. 便于计算各个顶点的度。对于无向图,邻接矩阵第i行元素之和就是顶点i的度;对于有向图,第i行 元素之和就是顶点 i 的出度,第i 列元素之和就是顶点i的入度。

(2) 缺点

  1. 不便于增加和删除顶点。
  2. 不便于统计边的数目,需要扫描邻接矩阵所有元素才能统计完毕,时间复杂度为O(n^2)
  3. 空间复杂度高。如果是有向图,n个顶点需要n2个单元存储边。如果是无向图,因其邻接矩阵是对称的,所以对规模较大的邻接矩阵可以采用压缩存储的方法,仅存储下三角(或上三角)的元素,这样需要n(n-1)/2个单元即可。但无论以何种方式存储,邻接矩阵表示法的空间复杂度均为0(n^2),这对千稀疏图而言尤其浪费空间。

6.4.2邻接表

1.邻接表表示法

对图中的每个顶点vi都分别建立一个对应的单链表(对无向图称为“边表”,对有向图称为“出边表”)来存储所有邻接与顶点vi的边(对于有向图而言是指以vi为尾的弧),边表中的每个结点分别对应于邻接于顶点vi的一条边。边表中的每个结点主要包含两个域,其中邻接点域(adjvex)指示与顶点vi邻接的点在图中的位置,链域(nextedge)指示下一条边或弧的结点。如果是带权图,还可以再加一个域weight,表示从vi到adjvex这条边的权值。

(1)表头结点表:由所有表头结点以顺序结构的形式存储, 以便可以随机访问任一顶点的边链表。表头结点包括数据域 (data) 和链域 (firstarc) 两部分, 如图6.11 (a) 所示。其中, 数据域用于存储顶点 V;的名称或其他有关信息;链域用千指向链表中第一个结点(即与顶点 V;邻接的第一个邻接点)。

(2)边表:由表示图中顶点间关系的 2n个边链表组成。 边链表中边结点包括邻接点域(adjvex)、数据域 (info)和链域 (nextarc)三部分, 如图6.11(b)所示。其中, 邻接点域指示与顶点 V;邻接的点在图中的位置;数据域存储和边相关的信息, 如权值等;链域指示与顶点v,邻接的下一条边的结点。

II- - - - -图的邻接表存储表示- ----
#define MVNum 100//最大顶点数
typedef struct ArcNode//边结点int adjvex; //该边所指向的顶点的位置
struct ArcNode * nextarc; //指向下一条边的指针
Otherinfo info; //和边相关的信息
}ArcNode; 
typedef struct VNode //顶点信息
{ 
VerTexType data; 
ArcNode *firstarc; //指向第一条依附该顶点的边的指针
) VNode,AdjList[MVNum]; //AdjList表示邻接表类型
typedef struct //邻接表
AdjList vertices; 
int vexnum,arcnum; //图的当前顶点数和边数
}ALGraph; 

2.采用邻接表表示法创建无向图

基于上述的邻接表表示法, 要创建一个图则需要创建其相应的顶点表和边表。下面以一个无向图为例来说明采用邻接表表示法创建无向图的算法。

算法6.2 采用邻接表表示法创建无向图

算法步骤

1.输入总顶点数和总边数。

2.依次输入点的信息存入顶点表中,使每个表头结点的指针域初始化为NULL。

3.创建邻接表。依次输入每条边依附的两个顶点, 确定这两个顶点的序号l和)之后, 将此边结点分别插入 Vi和vj对应的两个边链表的头部。

算法描述
Status CreateUDG(ALGraph &G) 
{//采用邻接表表示法, 创建无向图 G
cin>>G.vexnum>>G.arcnum; //输入总顶点数, 总边数
for(i=O;i<G.vexnum;++i) //输入各点,构造表头结点表
{
cin»G.vertices[i] .data; //输入顶点值
G.vertices[i] .firstarc=NULL;//初始化表头结点的指针域为NULL
}//for 
for(k=O;k<G.arcnum;++k) //输入各边, 构造邻接表
{
cin>>vl>>v2; //输入 一条边依附的两个顶点 
i=LocateVex(G,vl); j =LocateVex(G,v2); //确定vl和 v2在G中位置, 即顶点在G.vertices中的序号
pl=new ArcNode;//生成一个新的边结点*pl
pl->adjvex=j; //邻接点序号为
pl->nextarc=G. vertices [i] . firstarc; G. vertices [i] . firstarc= pl; //将新结点*pl插入顶点Vi的边表头部
p2=new ArcNode; //生成另一个对称的新的边结点*p2
p2->adjvex=i; //邻接点序号为1
p2->nextarc=G.vertices[j] .firstarc; G.vertices[j] .firstarc=p2; //将新结点*p2插入顶点Vj的边表头部
}//for 
return OK;
}

3.邻接表表示法的优缺点

(1) 优点

1.便于增加和删除顶点。

2.便于统计边的数目,按顶点表 顺 序扫描 所 有边表可得到边的数目,时间复杂度为O(n + e)。

3.空间效率高。对于一个具有n个顶点e条边的图 G, 若 G 是无向图,则在其邻接表表示中有 n 个顶点表结点和 2e 个边表结点;若 G 是有向图,则在它的邻接表表示或逆邻接表表示中均有 n 个顶点表结点和e个边表结点。因此,邻接表或逆邻接表表示的空间复杂度为 O(n + e), 适合表示稀疏图。对于稠密图,考虑到邻接表中要附加链域,因此常采取邻接矩阵表示法。

(2)缺点

1.不便于判断顶点之间是否有边,要判定 Vi和vj之间是否有边,就需扫描第i个边表,最坏情况下要耗费 O(n)时间。

2.不便于计算有向图各个顶点的度。对千无向图,在邻接表表示中顶点V;的度是第i个边表 中的结点个数。 在有向图的邻接表中,第 i 个边表上的结点个数是顶点 V;的出度,但求 vi;的入度较困难,需遍历各顶点的边表。若有向图采用逆邻接表表示,则与邻接表表示相反,求顶点的入度容易,而求顶点的出度较难。

6.4.3十字链表

十字链表 (Orthogonal List)是有向图的另一种链式存储结构。可以看成是将有向图的邻接表和逆邻接表结合起来得到的一种链表。 在十字链表中,对应千有向图中每一条弧有一个结点,对应于每个顶点也有一个结点。

有向图的十字链表存储表示的形式说明如下所示:

//- - - - -有向图的十字链表存储表示- ----
#define MAX_ VERTEX_NUM 20 
typedef strut ArcBox 
{
int tailvext,headvex; //该弧的尾和头顶点的位置
struct ArcBox *hlink, *tlink; //分别为弧头相同和弧尾相同的弧的链域
InfoType *info; //该弧相关信息的指针
}ArcBox; 
typedef struct VexNode 
VertexType data; 
ArcBox *firstin,*firstout; //分别指向该顶点第一条人弧和出弧
}VexNode; 
typedef struct 
{ 
VexNode xlist [MAX_VERTEX_NUM]; //表头向扯
int vexnnm, arcnum; / /有向图的当前顶点数和弧数
}OLGraph; 

6.4.4邻接多重表

邻接多重表 (Adjacency Multilist) 是无向图的另一种链式存储结构。虽然邻接表是无向图的一种很有效的存储结构,在邻接表中容易求得顶点和边的各种信息。但是,在邻接表中每一条边(Vi, vj )有两个结点,分别在第i个和第丿个链表中,这给某些图的操作带来不便。例如在某些图的应用问题中需要对边进行某种操作,如对已被搜索过的边作记号或删除一条边等,此时需要找到表示同一条边的两个结点。 因此,在进行这一类操作的无向图的问题中采用邻接多重表作存储结构更为适宜。

//- - - - -无向图的邻接多重表存储表示-----
#define MAX_VERTEX_NUM 20
typedef enum{unvisited,visited} Visitlf;
typedef struct EBox
{
Visitlf mark; //访问标记
int ivex, jvex; //该边依附的两个顶点的位置
struct EBox *ilink, *jlink; //分别指向依附这两个顶点的下一条边
InfoType *info; //该边信息指针
} Ebox; 
typedef struct VexBox 
{ 
VertexType data; 
EBox *firstedge; //指向第一条依附该顶点的边
}VexBox; 
typedef struct{ 
VexBox adjmulist [MAX_VERTEX_NUM]; 
int vexnum, edgenum; / /无向图的当前顶点数和边数
}AMLGraph;
posted @     阅读(242)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示