【前言】
图状结构是一种比树形结构更复杂的非线性结构。
在树状结构中,结点间具有分支层次关系,每一层的结点可以和下一层的多个结点相关,但只能和上一层中的一个结点相关。
而在图状结构中,任意两个结点之间都可能相关,即结点之间的邻接关系可以是任意的。
一、邻接矩阵
图和树一样,没有顺序映像的存储结构,但可以借助数组表示元素之间的关系。
邻接矩阵是用于描述图中顶点之间关系(即弧或边的权)的矩阵。
若图G中有n个顶点,则邻接矩阵是一个n×n的方阵,定义为:
若图G是一个有n个顶点的带权图(即网),有n个顶点,则邻接矩阵可定义为:
图的邻接矩阵存储方式是用两个数组来表示图:
- 一个一维数组vex:存储图中顶点信息;
- 一个二维数组arc:存储图中顶点之间关系(即弧或边)的信息。
//邻接矩阵表示法
//有向网
#include<iostream>
#include<cstdio>
using namespace std;
#define INFINITY 32767 //最大值,假定为无穷大
const int maxn = 10;
typedef int VertexType; //顶点类型
typedef int VRType; //顶点关系类型,对于无权图,用0或1表示相邻否;对于带权图,则为相应权值
struct Graph{ //邻接矩阵表示的图结构
VertexType vex[maxn]; //存储顶点
int arc[maxn][maxn]; //邻接矩阵
int vexnum,arcnum; //图的当前顶点数和弧数
};
int locateVex(Graph g,VertexType v) //若图中存在v,则返回v在图中的位置信息
{
for(int i=0;i<g.vexnum;i++){
if(v == g.vex[i]){
return i;
}
}
return -1; //图中无该顶点
}
void createGraph(Graph &g) //构建有向网g
{
cout<<"请输入顶点数和边数:";
cin>>g.vexnum>>g.arcnum;
//构造顶点向量
cout<<"请依次输入各顶点:\n";
for(int i=0;i<g.vexnum;i++){
scanf("%d",&g.vex[i]);
}
//初始化邻接矩阵
for(int i=0;i<g.vexnum;i++){
for(int j=0;j<g.vexnum;j++){
g.arc[i][j] = INFINITY;
}
}
//构造邻接矩阵
VertexType u,v; //分别是一条弧的弧尾(起点)和弧头(终点)
VRType w; //对于无权图,用0或1表示相邻否;对于带权图,则为相应权值
printf("每一行输入一条弧依附的顶点(先弧尾,再弧头)和权值(如:u v w):\n");
for(int i=0;i<g.arcnum;i++){
cin>>u>>v>>w;
int v1_index = locateVex(g,u);
int v2_index = locateVex(g,v);
g.arc[v1_index][v2_index] = w;
}
}
void print(Graph g)
{
cout<<"打印有向网g的邻接矩阵:\n";
for(int i=0;i<g.vexnum;i++){
for(int j=0;j<g.vexnum;j++){
if(g.arc[i][j] != INFINITY)
printf("%5d",g.arc[i][j]);
else{
printf(" -1"); //表示两点之间不直接相连
}
}
printf("\n");
}
printf("\n");
}
int main()
{
Graph g;
createGraph(g);
print(g);
return 0;
}
若现在有一个有向网及它的邻接矩阵如下图所示:
那么,执行上面程序,我们得到:
二、邻接表
对于图来说,邻接矩阵是不错的一种图存储结构,但是我们也发现,对于边数相对顶点较少的图,这种结构是存在对存储空间的极大浪费的。
因此我们考虑另外一种存储结构方式:邻接表,即数组与链表相结合的存储方法。
图的邻接表存储方式是用一个数组和一个单链表来表示图:
- 图中顶点用一个一维数组存储,另外,对于顶点数组中,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该顶点的边信息。
- 图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储。无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
例如,下图就是一个无向图的邻接表的结构。
从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点(即此顶点的第一个邻接点)。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。
若是有向图,邻接表的结构是类似的,如下图。
以顶点作为弧尾来存储边表容易得到每个顶点的出度,而以顶点为弧头的表容易得到顶点的入度,即逆邻接表。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。
以无向网为例,可有如下邻接表:
类似树的孩子链表。即对图中的每个顶点vi建立一个单链表,表中结点表示依附于该顶点vi的边或弧。
顶点结点(弧链表表头结点), 弧结点。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define INFINITY 32767 //最大值,假定为无穷大
const int maxn = 10; //最大顶点数
typedef int VertexType; //顶点类型
typedef int VRType; //边上的权值类型,对于带权图或网,则为相应权值
typedef struct ArcNode{ //边表结点,亦指弧节点信息
int adjvex; //邻接点域,存储该顶点对应的下标,亦指该弧所指向的顶点的在图中位置
VRType w; //用于存储权值,对于非网图可以不需要
struct ArcNode *nextarc; //链域,指向下一个邻接点;指向下一条弧的指针
}ArcNode;
typedef struct VNode{ //顶点表结点,亦指顶点节点信息
VertexType data; //顶点域,存储顶点信息
ArcNode *firstarc; //边表头指针,指向第一条依附该顶点的弧的指针
}VNode;
//VNode AdjVexList[maxn];
struct Graph{ //邻接表表示的图
VNode vex[maxn]; //顶点向量
int vexnum,arcnum; //图中当前顶点数和边数
};
int locateVex(Graph g,VertexType v) //若图中存在v,则返回v在图中的位置信息
{
for(int i=0;i<g.vexnum;i++){
if(v == g.vex[i].data){
return i;
}
}
return -1; //图中无该顶点
}
void createGraph(Graph &g) //构建无向网g
{
cout<<"请输入顶点数和边数(空格分隔):";
cin>>g.vexnum>>g.arcnum;
//构造顶点向量,并初始化
cout<<"请依次输入各顶点:\n";
for(int i=0;i<g.vexnum;i++){
scanf("%d",&g.vex[i].data);
g.vex[i].firstarc = NULL; //将边表置为空表,初始化为空指针
}
//构造邻接表,亦指建立边表
VertexType u,v; //分别是一条弧的弧尾和弧头(起点和终点)
VRType w; //对于无权图或网,用0或1表示相邻否;对于带权图或网,则为相应权值
printf("每一行输入一条弧依附的顶点(先弧尾,再弧头)和权值(如:u v w):\n");
for(int i=0;i<g.arcnum;i++){
cin>>u>>v>>w;
int v1_index = locateVex(g,u); //弧起点
int v2_index = locateVex(g,v); //弧终点
//采用“头插法”在各个顶点的弧链头部插入弧结点
ArcNode *p1 = (ArcNode *)malloc(sizeof(ArcNode)); //构造一个弧结点,作为弧vivj的弧头(终点)
p1->adjvex = v2_index; //邻接序号为v2_index
p1->w = w;
/* 将p1的指针指向当前顶点上指向的结点 */
p1->nextarc = g.vex[v1_index].firstarc;
g.vex[v1_index].firstarc = p1; //将当前顶点的指针指向p1
ArcNode *p2 = (ArcNode *)malloc(sizeof(ArcNode)); //构造一个弧结点,作为弧vivj的弧尾(起点)
p2->adjvex = v1_index;
p2->w = w;
p2->nextarc = g.vex[v2_index].firstarc;
g.vex[v2_index].firstarc = p2;
}
}
//打印邻接表
void print(Graph g)
{
cout<<"\n";
for(int i=0;i<g.vexnum;i++){
printf("依赖顶点%d的弧为:",g.vex[i].data);
ArcNode *p = g.vex[i].firstarc;
while(p){
printf("%d---%d(weight:%d) ",g.vex[i].data,g.vex[p->adjvex].data,p->w);
p = p->nextarc;
}
printf("\n");
}
printf("\n");
}
int main()
{
Graph g;
createGraph(g);
print(g);
return 0;
}
若现有一个无向网及其邻接表如下图所示,
执行上面程序,有:
三、其他
十字链表点击
邻接多重表及十字链表点击