1.邻接矩阵存储:
邻接矩阵是表示顶点之间相邻关系的矩阵。设G=(V,E)是具有n(n>0)个顶点的图,顶点的编号依次为0~n-1。
G的邻接矩阵A是n阶方阵,其定义如下:
1)如果G是无向图,则:
A[i][j]=1:若(i,j)∈E(G) 0:其他
(2)如果G是有向图,则:
A[i][j]=1:若<i,j>∈E(G) 0:其他
(3)如果G是带权无向图,则:
A[i][j]= wij :若i≠j且(i,j)∈E(G) 0:i=j ∞:其他
(4)如果G是带权有向图,则:
A[i][j]= wij :若i≠j且<i,j>∈E(G) 0:i=j ∞:其他
图的邻接矩阵存储类型定义如下:
#define MAXV <最大顶点个数>
typedef struct //---------------------------声明顶点的类型
{ int no; //顶点编号
InfoType info; //顶点其他信息
} VertexType;
typedef struct //图的定义---------------------------声明的邻接矩阵类型
{ int edges[MAXV][MAXV]; //邻接矩阵
int n,e; //顶点数,边数
VertexType vexs[MAXV]; //存放顶点信息
} MatGraph;
邻接表:
对图中每个顶点i建立一个单链表,将顶点i的所有邻接点链起来。
每个单链表上添加一个表头结点(表示顶点信息)。并将所有表头结点构成一个数组,下标为i的元素表示顶点i的表头结点。
例如:
邻接表的特点如下:
1、邻接表表示不唯一。
2、特别适合于稀疏图存储。 -------------- 邻接表的存储空间为O(n+e)。
图的邻接表存储类型定义如下:
typedef struct ANode//------------------------声明边结点类型
{ int adjvex; //该边的终点编号
struct ANode *nextarc; //指向下一条边的指针
InfoType weight; //该边的权值等信息
} ArcNode;
typedef struct Vnode//-----------------------------声明邻接表头结点类型
{ VertexType data; //顶点信息
ArcNode *firstarc; //指向第一条边
} VNode;
typedef struct //----------------------------声明图邻接表类型
{ VNode adjlist[MAXV]; //邻接表(有多少个顶点就有多少个元素)
int n,e; //图中顶点数n和边数e
} AdjGraph;
一个邻接表通常用指针引用:
逆邻接表: 就是在有向图的邻接表中,对每个顶点,链接的是指向该顶点的边。
对于无向图的邻接表的存储结构来说,当删除或者增加一条边的时候,其实会在两个地方删除。这样多少都会比较麻烦,那么就引入了下面的 邻接多重表 :
邻接多重表是无向图的另外一种存储结构:
邻接多重表对每一条变只存储一次,每一条边存储在两个链表中,不论是蓝色的还是绿色的先指向的都是链表形式。
上面的绿色的存储域中,有空着的,可以写边的权值信息,还有两个域分别写边的两个顶点的信息
当给顶点了编号(1234这样的或者ABCD等等)后,就相当于顶点就有了先后关系
通常都按照编号的顺序来说,或者是对于上面的结点2,他的临结点就有3,1,4。现在已经编好号了,就可以说2的第一个邻结点为1,第二个邻结点就是3,第三个邻结点就是4,按照顺序决定一个结点的邻结点的先后顺序
按照顺序,先是0,横线链表写出0的邻结点1和3的信息
然后再按照顺序找处1结点的邻结点0,2,4的信息
因为一开始已经写了0与1这条边的信息,所以1直接指向0的邻结点的存储中的0与1
再重复进行上述的判断即可
所以对于无向图的存储来说,还是邻接多重表的存储更容易找出顶点的度与边的信息
对于有向图的存储结构来说,很容易找出出度,但是很难找出入读。而对于有向图的逆邻接表的存储结构来说很容易找出入度,但很难找出出度。那么下面介绍一种存储结构,既能轻松找出出度也能找出入度,那就是:十字链表:
十字链表是有向图的另外一种存储结构,它是邻接表和逆邻接表的结合。
红色箭头指向的链表表示的是 出边
绿色箭头指向的链表表示的是 入边
练习:
注意:按照顺序进行连接。