图的存储表示及示例


图的存储

图的存储必须要完整、准确的反映顶点集和边集的信息。
无论是有向图还是无向图,主要的存储方式都有两种:邻接矩阵邻接表
前者属于图的顺序存储结构,后者属于图的链接存储结构。


邻接矩阵法

所谓邻接矩阵存储,是指用一个一维数组存储图中顶点的信息,用一个二维数组存储图中边的信息(即各个顶点之间的邻接关系),存储顶点之间邻接关系的二维数组称为邻接矩阵

若图G是一个有n个顶点的图,那么图的邻接矩阵是一个n阶方阵A。
A [ i ] [ j ] = { 1 , 若 ( v i , v j ) 或 < v i , v j > 是 E 中 的 边 0 , 若 ( v i , v j ) 或 < v i , v j > 不 是 E 中 的 边 A[i][j]=\begin{cases} 1,\quad 若(v_i,v_j)或<v_i,v_j>是E中的边\\ 0, \quad若(v_i,v_j)或<v_i,v_j>不是E中的边\\ \end{cases} A[i][j]={1,vi,vj<vi,vj>E0,vi,vj<vi,vj>E

例如:

   

A 1 = [ 0 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 ] A 2 = [ 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 ] A_1=\begin{bmatrix} 0&1&1&1\\ 1&0&0&0\\ 1&0&0&1\\ 1&0&1&0\\ \end{bmatrix} \quad\quad\quad A_2=\begin{bmatrix} 0&1&1&0\\ 0&0&0&0\\ 0&0&0&1\\ 1&0&0&0\\ \end{bmatrix} A1=0111100010011010A2=0001100010000010

不难看出,无向图的邻接矩阵是对称的,而有向图的邻接矩阵不一定对阵。
这是因为在无向图中, ( v i , v j ) = ( v j , v i ) (v_i,v_j)=(v_j,v_i) vi,vj=vj,vi,是同一条边,故一定有 A [ i ] [ j ] = A [ j ] [ i ] A[i][j] = A[j][i] A[i][j]=A[j][i]

对于带权图,若 v i v_i vi v j v_j vj之间有边相连,则邻接矩阵中对应项存放着该边对应的权值,若顶点 v i v_i vi v j v_j vj不相连,则用 ∞ \infty 来代表这两个顶点之间不存在边:
A [ i ] [ j ] = { w i j ,   若 ( v i , v j ) 或 < v i , v j > 是 E 中 的 边 0 , 若 ( v i , v j ) 或 < v i , v j > 不 是 E 中 的 边 且 i = j ∞ , 若 ( v i , v j ) 或 < v i , v j > 不 是 E 中 的 边 且 i ≠ j A[i][j]=\begin{cases} w_{ij},\quad\ 若(v_i,v_j)或<v_i,v_j>是E中的边\\ 0, \quad\quad若(v_i,v_j)或<v_i,v_j>不是E中的边且i=j\\ \infty,\quad若(v_i,v_j)或<v_i,v_j>不是E中的边且i≠j\\ \end{cases} A[i][j]=wij, vi,vj<vi,vj>E0,vi,vj<vi,vj>Ei=jvi,vj<vi,vj>Ei=j

例如:

A 3 = [ 0 12 8 ∞ ∞ 0 ∞ ∞ ∞ ∞ 0 9 4 ∞ ∞ 0 ] A_3=\begin{bmatrix} 0&12&8&\infty\\ \infty&0&\infty&\infty\\ \infty&\infty&0&9\\ 4&\infty&\infty&0\\ \end{bmatrix} A3=041208090

图的邻接矩阵存储结构定义如下:

#define maxVertexNum 30 //图中顶点数目的最大值
typedef char VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上权值的数据类型

typedef struct{
    int vexnum,edgenum; //图中实际顶点个数和边的条数
    VertexType Vextex[maxVertexNum]; //顶点表
    EdgeType Edge[maxVertexNum][maxVertexNum]; //邻接矩阵
}MGraph;

邻接矩阵表示法的空间复杂度为 O ( n 2 ) O(n^2) O(n2),其中n为图的顶点数。


邻接矩阵表示法的特点

1、 无向图的邻接矩阵一定是一个对称矩阵,并且唯一。因此,在实际存储邻接矩阵时只需存储上(或下)三角矩阵的元素。
2、对于无向图,邻接矩阵的第 i 行(或第 i 列)非零元素的个数正好是第 i 个顶点的度。
3、 对于有向图,邻接矩阵的第 i 行(或第 i 列)非零元素的个数正好是第 i 个顶点的出度(或入度)。
4、邻接矩阵法存储图,很容易确定两个顶点之间是否有边相连,但要确定图中有多少条边,必须按行按列对每个元素进行检测,所花费的时间代价很大。这是使用邻接矩阵存储图的局限性。
5、稠密图适合用邻接矩阵的存储表示。


邻接表法

当一个图为稀疏图时,使用邻接矩阵表示法显然要浪费大量的存储空间,而图的邻接表法结合了顺序存储和链式存储方法,大大减少了这种不必要的浪费。

邻接表:是指对图G中每个顶点 v i {v_i} vi建立一个单链表,第 i 个单链表中的结点表示依附于顶点 v i {v_i} vi的边(对有向图来说则是以顶点 v i {v_i} vi为尾的弧),这个单链表就称为顶点 v i {v_i} vi边表(对于有向图则称为出边表)。
边表的头指针和顶点的数据信息采用顺序存储(称为顶点表),所以在邻接表中存在两种结点:顶点表结点边表结点

顶 点 域 边 表 头 指 针 d a t a f i r s t a r c 邻 接 域 指 针 域 a d j v e x n e x t a r c \begin{array}{|c|c|} \hline {顶点域}&{边表头指针}\\ \hline {data}&{firstarc}\\ \hline \end{array} \quad\quad \begin{array}{|c|c|} \hline {邻接域}&{指针域}\\ \hline {adjvex}&{nextarc}\\ \hline \end{array} datafirstarcadjvexnextarc

有时候,对于网(带权图),其边表会增加权值项,即
邻 接 域 信 息 域 指 针 域 a d j v e x i n f o n e x t a r c \begin{array}{|c|c|c|} \hline {邻接域}&{信息域}&{指针域}\\ \hline {adjvex}&{info}&{nextarc}\\ \hline \end{array} adjvexinfonextarc

例如

图的邻接表存储结构定义如下:

typedef struct ENode{  //边表结点
    int adjvex;  //弧指向的顶点的位置
    struct ENode *nextarc; //指向下一条弧的指针
    //InfoType info; //网的边权值
}EdgeNode;

typedef struct VNode{  //顶点表结点
    VertexType data; //顶点的数据
    struct ENode *firstarc; //边链表的头指针
}VertexNode;

typedef struct{ 
    VertexNode VertexList[maxVertexNum];  //顶点表
    int vexnum,edgenum; //图的顶点数和边数
}ALGraph; //以邻接表存储的图类型

邻接表存储的特点

1、若G为无向图,则所需的存储空间为O(|V| + 2|E|),因为每条边在邻接表中出现了两次;若G为有向图,则所需的存储空间为O(|V| + |E|)。
2、稀疏图通常适合用邻接表表示,可以极大节省存储空间。
3、在邻接表中,给定一个结点,能很容易找出它的所有邻边,只需要读取它的邻接表;但是,若要确定给定的两个顶点之间是否存在边,则在邻接表中需要在相应结点对应的边表中查找另一结点,效率较邻接矩阵低。
4、求一个给定顶点的出度只需要计算其邻接表中的结点个数;求其顶点的入度则需要遍历全部的邻接表。因此,也有人采用逆邻接表的存储方式来加速求解给定顶点的入度。
5、图的邻接表表示并不唯一,因为在每个顶点对应的单链表中,各边结点的链接次序可以是任意的,它取决于建立邻接表的算法及边的输入次序。


无向图的邻接多重表表示

邻接多重表是无向图的另一种链式存储结构,主要用在基于边的图算法中。
在邻接表中,容易求的顶点和边的各种信息,但是在邻接表中求两个顶点之间是否存在边时,需要分别在两个顶点的边表中遍历,效率很低。

在邻接多重表中,每条边用一个结点表示,即用边结点表示。
标 记 域 顶 点 域 指 针 域 顶 点 域 指 针 域 m a r k v e r t e x 1 l i n k 1 v e r t e x 2 l i n k 2 \begin{array}{|c|c|c|c|c|} \hline {标记域}&{顶点域}&{指针域}&{顶点域}&{指针域}\\ \hline {mark}&{vertex1}&{link1}&{vertex2}&{link2}\\ \hline \end{array} markvertex1link1vertex2link2

边结点:

  • mark:标志域,可用于标记该边是否被处理过
  • vertex1、vertex2:顶点域,用于指明该边的两个顶点在图中的位置
  • link1、link2:指针域,分别指向下一条依附于顶点vertex1和vertex2的边

顶 点 域 指 针 域 d a t a f i r s t e d g e \begin{array}{|c|c|} \hline {顶点域}&{指针域}\\ \hline {data}&{firstedge}\\ \hline \end{array} datafirstedge

顶点结点:

  • data:数据域,存储该顶点的信息
  • firstedge:指针域,指向第一条依附于该顶点的边

例如:
在这里插入图片描述

邻接多重表的存储结构定义如下:

typedef struct ENode{ //边结点
    int mark; //标志域
    int vertex1, vertex2; //该边依附的两个顶点
    struct ENode *link1, *link2; //分别指向两个顶点的下一条边
}EdgeNode;

typedef struct VNode{ //顶点结点
    VertexType data;  //顶点信息
    struct ENode *firstedge;  //指向第一条依附于该顶点的边
}VertexNode;

typedef struct{ 
    VertexNode VertexLink[maxVertexNum]; //顶点表
    int vexnum, edgenum; //图的顶点数和边数
}AMLGraph;//以邻接多重表存储的图类型

邻接多重表的特点

1、在邻接多重表中,所有依附于同一顶点的边都链接在同一个单链表中。只要从某个顶点 i 出发,就可以找出依附于该顶点的所有边,以及它的所有邻接顶点。
2、由于每条边依附于两个结点,所以每个边结点同时链接在两个链表中。


有向图的十字链表

十字链表是有向图的一种链式存储结构。在十字链表中,对应于有向图中的每条弧有一个结点,对应于每个顶点也有一个结点,结点结构如下所示:
标 记 域 尾 域 头 域 指 针 域 指 针 域 m a r k t a i l v e x h e a d v e x h l i n k t l i n k \begin{array}{|c|c|c|c|c|} \hline {标记域}&{尾域}&{头域}&{指针域}&{指针域}\\ \hline {mark}&{tailvex}&{headvex}&{hlink}&{tlink}\\ \hline \end{array} marktailvexheadvexhlinktlink

边结点:

  • mark:标记域,可用于标记是否被处理过
  • tailvex、headvex:尾域和头域,分别指示弧尾和弧头这两个顶点在图中的位置
  • hlink:指向弧头相同的下一条弧
  • tlink:指向弧尾相同的下一条弧

顶 点 域 指 针 域 指 针 域 d a t a f i r s t i n f i r s t o u t \begin{array}{|c|c|c|} \hline {顶点域}&{指针域}&{指针域}\\ \hline {data}&{firstin}&{firstout}\\ \hline \end{array} datafirstinfirstout

顶点结点:

  • data:数据域,存储该顶点的信息
  • firstin:指针域,指向以该顶点为弧头的第一个弧结点
  • firstout:指针域,指向以该顶点为弧尾的第一个弧结点

例如:
在这里插入图片描述

图的十字链表存储结构定义如下:

typedef struct ENode{  //边结点
    int mark; //标记
    int tailvex, headvex; //弧的弧头和弧尾的位置
    struct ENode *hlink, *tlink; //分别指向弧头相同和弧尾相同的结点
}EdgeNode;

typedef struct VNode{  //顶点结点
    VertexType data; //顶点信息
    struct ENode *firstin, *firstout; //分别指向第一条弧头或弧尾相同的下一条边
}VertexNode;

typedef struct{
    VertexNode VertexLink[maxVertexNum];  //顶点表
    int vexnum, edgenum; //图的顶点数和边数
}GLGraph; //以十字链表存储的图类型

十字链表存储的特点

在十字链表中,既容易找到以 v i {v_i} vi为尾的弧,又容易找到以 v i {v_i} vi为头的弧,因而容易求的顶点的出度和入度。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值