三。图的存储结构
1.邻接矩阵(数组)表示法
建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点之间关系)
- 设图A=(%月有n个顶点,则
- 图的邻接矩阵是一 个二维数组A. arcs[n][n], 定义为:
分析1:无向图的邻接矩阵是对称的;
分析2:顶点i的度=第i行(列)中1的个数;
特别:完全图的邻接矩阵中,对角元素为0,其余1。
注:在有向图的邻接矩阵中,
第i行含义:以结点vi为尾的弧(即出度边) ;
第列含义:以结点vi为头的弧(即入度边)。
分析1:有向图的邻接矩阵可能是不对称的。
分析2:顶点的出度=第i行元素之和
顶点的入度=第列元素之和
顶点的度=第i行元素之和+第列元素之和
2.邻接矩阵的(数组)存储表示:
(用两个数组分别存储顶点表和邻接矩阵)
采用邻接矩阵表示法创建无向网
算法思路:
(1)输入总顶点数和总边数。
(2)依次输入点的信息存入顶点表中。
(3)初始化邻接矩阵,使每个权值初始化为极大值。
(4)构造邻接矩阵。
AMGraph.h:
#include <stdio.h>
#define MAX_VERtEX_NUM 20 //顶点的最大个数
#define VRType int //表示顶点之间的关系的变量类型
#define InfoType char //存储弧或者边额外信息的指针变量类型
#define VertexType int //图中顶点的数据类型
typedef enum{ DG, DN, UDG, UDN }GraphKind; //枚举图的 4 种类型
typedef struct {
VRType adj; //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。
InfoType * info; //弧或边额外含有的信息指针
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];
typedef struct {
VertexType vexs[MAX_VERtEX_NUM]; //存储图中顶点数据
AdjMatrix arcs; //二维数组,记录顶点之间的关系
int vexnum, arcnum; //记录图的顶点数和弧(边)数
GraphKind kind; //记录图的种类
}MGraph;
AMGraph.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"AMGraph.h"
//根据顶点本身数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph * G, VertexType v)
{
int i = 0;
//遍历一维数组,找到变量v
for (; i<G->vexnum; i++)
{
if (G->vexs[i] == v)
{
break;
}
}
//如果找不到,输出提示语句,返回-1
if (i>G->vexnum)
{
printf("no such vertex.\n");
return -1;
}
return i;
}
//构造无向网。和无向图唯一的区别就是二阶矩阵中存储的是权值
void CreateUDN(MGraph* G)
{
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
for (int i = 0; i < G->vexnum; i++)
{
scanf("%d", &(G->vexs[i]));
}
for (int i = 0; i < G->vexnum; i++)
{
for (int j = 0; j < G->vexnum; j++)
{
G->arcs[i][j].adj = 0;
G->arcs[i][j].info = NULL;
}
}
for (int i = 0; i < G->arcnum; i++)
{
int v1, v2, w;
scanf("%d,%d,%d", &v1, &v2, &w);
int m = LocateVex(G, v1);
int n = LocateVex(G, v2);
if (m == -1 || n == -1)
{
printf("no this vertex\n");
return;
}
G->arcs[n][m].adj = w;
G->arcs[m][n].adj = w;//矩阵对称
}
}
//构造有向网,和有向图不同的是二阶矩阵中存储的是权值。
void CreateUDG(MGraph *G)
{
scanf("%d,%d", &(G->vexnum), &(G->arcnum));
for (int i = 0; i < G->vexnum; i++)
{
scanf("%d", &(G->vexs[i]));
}
for (int i = 0; i < G->vexnum; i++)
{
for (int j = 0; j < G->vexnum; j++)
{
G->arcs[i][j].adj = 0;
G->arcs[i][j].info = NULL;
}
}
for (int i = 0; i < G->arcnum; i++)
{
int v1, v2, w;
scanf("%d,%d,%d", &v1, &v2, &w);
int n = LocateVex(G, v1);
int m = LocateVex(G, v2);
if (m == -1 || n == -1) {
printf("no this vertex\n");
return;
}
G->arcs[n][m].adj = w;
}
}
3.邻接矩阵的优缺点
优点
■直观、简单、好理解
■方便检查任意- 对顶点间是否存在边
■方便找任一顶点的所有“邻接点”(有边直接相连的顶点)
■方便计算任一顶点的"度”(从该点发出的边数为“出度”,指向该点的边数为"入度”)
无向图:对应行(或列)非0元素的个数
有向图:对应行非0元素的个数是“出度”;
对应列非0元素的个数是"入度"
缺点
■不便于增加和删除顶点
■浪费空间一存稀疏图 人点很多而边很少)有大量无效元素
对稠密图(特别是完全图)还是很合算的
■浪费时间---统计稀疏图中一共有多少条边
4.邻接表(链式)表示法
●顶点:
按编号顺序将顶点数据存储在一维数组中;
●关联同一顶点的边(以顶点为尾的弧) :
用线性链表存储
特点:
●邻接表不唯一
●若无向图中有n个顶点、e条边,则其邻接表需n个头结点和2e个表结点。适宜存储稀疏图。
●无向图中顶点vi的度为第i个单链表中的结点数。
特点:
顶点Vi的出度为第i个单链表中的结点个数。
顶点Vi的入度为整个单链表中邻接点域值是i-1的结点个数。
特点:
顶点Vi的入度为第i个单链表中的结点个数。
顶点Vi的出度为整个单链表中邻接点域值是i-1的结点个数。
5.邻接矩阵的(链式)存储表示:
1.
#define MAX_VERTEX_NUM 20//最大顶点个数
#define VertexType int//顶点数据的类型
#define InfoType int//图中弧或者边包含的信息的类型
typedef struct ArcNode{ //表结点
int adjvex;//邻接点在数组中的位置下标
struct ArcNode * nextarc;//指向下一个邻接点的指针
}ArcNode;
typedef struct VNode{ //头结点
VertexType data;//顶点的数据域
ArcNode * firstarc;//指向邻接点的指针
}VNode,AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组
2.
typedef struct ArcNode{ //表结点
int adjvex;//邻接点在数组中的位置下标
struct ArcNode * nextarc;//指向下一个邻接点的指针
InfoType * info;//信息域
}ArcNode;
图的结构定义:
typedef struct {
AdjList vertices;//图中顶点的数组
int vexnum,arcnum;//记录图中顶点数和边或弧数
int kind;//记录图的种类
}ALGraph;
如:一些操作
ALGragh G;//定义了邻接表表示的图G
G.vexum=5; G.arcnum=5; //图G包含5个顶点, 5条边
G.vertices[1].data= 'b'//图G中第2个顶点是b
p=G.vertices[1].firtarc;,//指针p指向顶点b的第一条边结点
p-> adjvex =4;//p指针所指边结点是到下标为4的结点的边
可采用邻接表表示法创建无向网
6.邻接矩阵与邻接表表示法的关系
1.联系:邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数。
2.区别:
①对于任一-确定的无向图,邻接矩阵是唯一的(行列号与顶点编号一致),但邻接表不唯一(链接次序与顶点编号无关)。
②邻接矩阵的空间复杂度为O(n2),而邻接表的空间复杂度为O(n+e)。数量级都为e
3.用途:邻接矩阵多用于稠密图;而邻接表多用于稀疏图