目录
一、定义和术语
二、存储结构
1、邻接矩阵
1.1、邻接矩阵优点
1.2、邻接矩阵缺点
2、邻接表
3、邻接矩阵和邻接表的区别和用途
3.1、区别
3.2、用途
三、宏定义
四、结构体定义
1、邻接矩阵
2、邻接表
3、网数据类型(造测试数据)
五、函数定义
1、使用邻接矩阵创建无向网
2、使用邻接表创建无向网
3、销毁使用邻接矩阵创建的无向网
4、销毁使用邻接表创建的无向网
六、Linux环境编译测试
一、定义和术语
名词 | 描述 |
图 | Graph = ( Vertex , Edge ) Vertex:顶点(数据元素)的有穷非空集合。 Edge:边的有穷集合。 |
无向图 | 每条边都是无方向的。 |
有向图 | 每条边都是有方向的。 |
完全图 | 任意两个点都有一条边相连。 无向完全图:n个顶点,n * (n - 1) / 2条边。 有向完全图:n个顶点,n * (n - 1)条边。 |
稀疏图 | 有很少的边或弧的图。 |
稠密图 | 有较多的边或弧的图。 |
网 | 边/弧带权的图。 |
邻接 | 有边/弧相连的两个顶点之间的关系。 存在(vi, vj),则称vi和vj互为邻接点。 存在<vi, vj>,则称vi邻接到vj,vj邻接到vi。 |
关联(依附) | 边/弧与顶点之间的关系。 存在(vi, vj) / <vi, vj>,则称该边/弧关联于vi和vj。 |
顶点的度 | 于该顶点相关联的边的数目,记作TD(v),在有向图中,顶点的度等于该顶点的入度于出度之和。 顶点v的出度是以v为始点的有向边的条数,记作OD(v)。 |
有向树 | 当有向图中仅一个顶点的入度为0,其余顶点的入度为1. |
路径 | 接续的边构成的顶点序列。 |
路径长度 | 路径上边或弧的数目/权值之和。 |
回路(环) | 第一个顶点和最后一个顶点相同路径。 |
简单路径 | 除路径起点和终点可以相同外,其余顶点均不相同的路径。 |
简单回路(简单环) | 除路径起点和终点相同外,其余顶点均不相同的路径。 |
连通图(强连通图) | 在无(有)向图G = (V, {E})中,若对任何两个顶点v,u都存在从v到u的路径,则称G是连通图(强连通图)。 |
权与网 | 图中边或弧所具有的相关数称为权。表明从一个顶点到另一个顶点的距离或耗费。 带权的图称为网。 |
子图 | 设有两个图G = (V, {E})、G1 = (V1, {E1}),V1∈V,E1∈E,则称G1是G的子图。 |
连通分量(强连通分量) | 无向图G的极大连通子图称为G的连通分量。 |
极大连通子图 | 该子图是G的连通子图,将G的任何不在该子图中的顶点加入,子图不再是连通。 |
强连通分量 | 有向图G的极大强连通子图称为G的强连通分量。 |
极大强连通子图 | 该子图是G的强连通子图,将G的任何不在该子图中的顶点加入,子图不再是强连通。 |
极小连通子图 | 该子图是G的连通子图,在该子图中删除任何一条边,子图不再连通。。 |
生成树 | 包含无向图G所有顶点的极小连通子图。 |
生成森林 | 对非连通图,由各个连通分量的生成树的集合。 |
二、存储结构
图的逻辑结构是多对多的,具体分类如下:
分类名 | 描述 |
数组存储结构 | 1、邻接矩阵。(本文介绍) |
链式存储结构 | 1、邻接表。(本文介绍) 2、邻接多重表。 3、十字链表。 |
这里以无向网为例,我们要画一个这样的。
测试数据:
举例说明一下{0,3,10},A->D且权值为10。
{'A','B','C','D','E'};
{{0,3,10},{0,1,20},{0,2,30},{2,3,10},{1,4,50},{3,4,20}};
1、邻接矩阵
建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点之间关系)。
生成的邻接矩阵如下:
VertexArray : [A ,B ,C ,D ,E ]
ArcArray :
[32767 ,20 ,30 ,10 ,32767 ]
[20 ,32767 ,32767 ,32767 ,50 ]
[30 ,32767 ,32767 ,10 ,32767 ]
[10 ,32767 ,10 ,32767 ,20 ]
[32767 ,50 ,32767 ,20 ,32767 ]
[32767 ,20 ,30 ,10 ,32767 ]第一行表示:A->A不通,A->B通,A->C通,A->D通,A->E不通。
特性1:无向网的邻接矩阵是对称的。
特性2:顶点i的度等于第i行中非极大值(32767)的个数。
特别:完全网的邻接矩阵中,对角元素为32767,其余皆为非32767。
1.1、邻接矩阵优点
(1)直观好理解。
(2)容易判断两个顶点间是否存在边或弧。
(3)容易查找任意顶点的所有邻接点。
(4)方便计算任意顶点的度。
1.2、邻接矩阵缺点
(1)添加和删除顶点不方便。
(2)存储稀疏图时浪费空间。
(3)统计稀疏图一共有多少条边浪费时间。
2、邻接表
顶点:按编号顺序将顶点数据存储在一维数组中。
关联同一顶点的边(以顶点为尾的弧):用线性链表存储。
生成的邻接表如下:
A : [ (2, 30, 0x15ac8b0),(1, 20, 0x15ac870),(3, 10, (nil))]
B : [ (4, 50, 0x15ac8d0),(0, 20, (nil))]
C : [ (3, 10, 0x15ac910),(0, 30, (nil))]
D : [ (4, 20, 0x15ac950),(2, 10, 0x15ac890),(0, 10, (nil))]
E : [ (3, 20, 0x15ac990),(1, 50, (nil))]
A : [ (2, 30, 0x15ac8b0),(1, 20, 0x15ac870),(3, 10, (nil))]第一行表示:A->C通、权值30、下一个结点指针,A->B通、权值20、下一个结点指针,A->D通、权值10、下一个结点指针。
特性1:邻接表不唯一。
特性2:若无向网中有n个顶点、e条边,则其邻接表需要n个头结点和2e个表结点。存储稀疏图比较合适。
特性3:无向网中顶点Vi的度为第i个单链表中的结点数。
特性4:邻接表出度易找,入度难找。逆邻接表出度难找,入度易找。
3、邻接矩阵和邻接表的区别和用途
3.1、区别
(1)对于任一确定的无向网,邻接矩阵是唯一的(行列号与顶点编号一致),但邻接表不唯一。
(2)邻接矩阵的空间复杂度是O(n^2),邻接表为O(n+e)。
3.2、用途
邻接矩阵多用于稠密图。
邻接表多用于稀疏图。
三、宏定义
#define MAX_INT_TYPE_NUM 32767 //网中代表无穷大,也代表顶点个数。
#define MAX_VERTEX_NUM 10000 //顶点数组中存放顶点的最大个数。
#define NET_DIRECTION_FLAG 0 //有向网的标志
#define NET_UNDIRECTION_FLAG 1 //无向网的标志
四、结构体定义
1、邻接矩阵
//邻接矩阵图
typedef struct AdjacencyMatrixGraph
{
VertexType VertexArray[MAX_VERTEX_NUM];
ArcType ArcArray[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
int CurVertexNum; //顶点数组中当前的顶点数。
int CurArcNum; //弧数组中当前的弧个数。
}AMGraph;
2、邻接表
//邻接表
typedef struct ArcNode
{
VertexIndexType EndVertexIndex;
WeightType Weight;
struct ArcNode* NextNodePtr;
}ArcNode;
typedef struct VertexNode
{
VertexType Vertex;
ArcNode* FirstArcNodePtr;
}VertexNode;
typedef struct AdjacencyGragh
{
VertexNode* Vertices;
int VertexNum; //邻接表中的顶点数。
int ArcNum; //邻接表中的弧个数。
}AGraph;
3、网数据类型(造测试数据)
//网数据类型
typedef struct NetArcDataType
{
VertexIndexType StartVertexIndex;//起始顶点。
VertexIndexType EndVertexIndex; //结束顶点。
WeightType Weight; //权值。
}NetArcDataType; //数据为网的弧或边的信息。
// NetArcDataType NetArcDataArray[MAX_VERTEX_NUM * (MAX_VERTEX_NUM - 1)]。
// 问题简记,如果MAX_VERTEX_NUM为1000时,在malloc NetDataType*类型内存时,会出现段错误。
typedef struct NetDataType
{
VertexType* VertexArray;
NetArcDataType* NetArcDataArray; //有向图完全图的边最多,顶点n,边最多为n*(n-1)。
int VertexNum; //顶点数据数组中的顶点数。
int ArcNum; //弧数据数组中的弧个数。
int DirectionFlag; //无向图还是有向图的标志。
}NetDataType; //数据为网的信息。
五、函数定义
1、使用邻接矩阵创建无向网
Status CreateUndirectionNetUseAMGraph(AMGraph** AMG, NetDataType NDT)
{
*AMG = (AMGraph*)MyMalloc(sizeof(AMGraph));
int i,j;
(*AMG)->CurVertexNum = NDT.VertexNum; //顶点数复制。
(*AMG)->CurArcNum = NDT.ArcNum * 2; //边数复制,由于无向图是双向的,所以需要乘2。
//顶点数组复制。
memcpy((*AMG)->VertexArray, NDT.VertexArray, sizeof(VertexType) * ((*AMG)->CurVertexNum));
//初始化网中弧或边数组,统一初始化为无穷大,这里设置为int的最大值32767。
for(i = 0; i < (*AMG)->CurVertexNum; i++)
{
for(j = 0; j < (*AMG)->CurVertexNum; j++)
{
(*AMG)->ArcArray[i][j] = MAX_INT_TYPE_NUM;
}
}
//把网数据中的弧或边数据填充到邻接矩阵图中。
for(i = 0; i < NDT.ArcNum; i++)
{
(*AMG)->ArcArray[NDT.NetArcDataArray[i].StartVertexIndex][NDT.NetArcDataArray[i].EndVertexIndex] = NDT.NetArcDataArray[i].Weight;
(*AMG)->ArcArray[NDT.NetArcDataArray[i].EndVertexIndex][NDT.NetArcDataArray[i].StartVertexIndex] = NDT.NetArcDataArray[i].Weight;
}
Log("Create Undirection Net Use AMGraph : OK\n",Info);
return SuccessFlag;
}
2、使用邻接表创建无向网
Status CreateUndirectionNetUseAGraph(AGraph** AG, NetDataType NDT)
{
(*AG) = (AGraph*)MyMalloc(sizeof(AGraph));
(*AG)->VertexNum = NDT.VertexNum;
(*AG)->ArcNum = NDT.ArcNum * 2;
//init adjacency graph
(*AG)->Vertices = (VertexNode*)MyMalloc(sizeof(VertexNode) * (*AG)->VertexNum);
int i;
for(i = 0; i < (*AG)->VertexNum; i++)
{
(*AG)->Vertices[i].Vertex = NDT.VertexArray[i];
(*AG)->Vertices[i].FirstArcNodePtr = NULL;
}
//add arc to adjacency graph
ArcNode* TmpArcNode = NULL;
for(i = 0; i < NDT.ArcNum; i++)
{
//printf("%d, %d, %d\n",NDT.NetArcDataArray[i].StartVertexIndex,NDT.NetArcDataArray[i].EndVertexIndex,NDT.NetArcDataArray[i].Weight);
ArcNode* NewArcNode = (ArcNode*)MyMalloc(sizeof(ArcNode));
NewArcNode->EndVertexIndex = NDT.NetArcDataArray[i].EndVertexIndex;
NewArcNode->Weight = NDT.NetArcDataArray[i].Weight;
NewArcNode->NextNodePtr = NULL;
TmpArcNode = (*AG)->Vertices[NDT.NetArcDataArray[i].StartVertexIndex].FirstArcNodePtr;
if(TmpArcNode == NULL)//说明弧结点链表头部为空
{
(*AG)->Vertices[NDT.NetArcDataArray[i].StartVertexIndex].FirstArcNodePtr = NewArcNode;
}
else
{
NewArcNode->NextNodePtr = TmpArcNode;
(*AG)->Vertices[NDT.NetArcDataArray[i].StartVertexIndex].FirstArcNodePtr = NewArcNode; //头插法,效率高。
}
ArcNode* NewArcNode1 = (ArcNode*)MyMalloc(sizeof(ArcNode));
NewArcNode1->EndVertexIndex = NDT.NetArcDataArray[i].StartVertexIndex;
NewArcNode1->Weight = NDT.NetArcDataArray[i].Weight;
NewArcNode1->NextNodePtr = NULL;
TmpArcNode = (*AG)->Vertices[NDT.NetArcDataArray[i].EndVertexIndex].FirstArcNodePtr;
if(TmpArcNode == NULL)//说明弧结点链表头部为空
{
(*AG)->Vertices[NDT.NetArcDataArray[i].EndVertexIndex].FirstArcNodePtr = NewArcNode1;
}
else
{
NewArcNode1->NextNodePtr = TmpArcNode;
(*AG)->Vertices[NDT.NetArcDataArray[i].EndVertexIndex].FirstArcNodePtr = NewArcNode1;
}
}
Log("Create Undirection Net Use AGraph : OK\n",Info);
return SuccessFlag;
}
3、销毁使用邻接矩阵创建的无向网
Status DestoryUndirectionNetUseAMGraph(AMGraph** AMG)
{
JudgeAllNullPointer(AMG);
JudgeAllNullPointer(*AMG);
free(*AMG);
*AMG = NULL;
Log("Destory Undirection Net Use AMGraph: OK\n",Info);
return SuccessFlag;
}
4、销毁使用邻接表创建的无向网
Status DestoryUndirectionNetUseAGraph(AGraph** AG)
{
JudgeAllNullPointer(AG);
JudgeAllNullPointer(*AG);
ArcNode* TmpArcNode = NULL;
ArcNode* CurArcNode = NULL;
//free All ArcNode
int i;
for(i = 0; i < (*AG)->VertexNum; i++)
{
(*AG)->Vertices[i].Vertex = '\0';
TmpArcNode = (*AG)->Vertices[i].FirstArcNodePtr;
CurArcNode = TmpArcNode;
while(CurArcNode != NULL)
{
TmpArcNode = CurArcNode->NextNodePtr;
free(CurArcNode);
CurArcNode = TmpArcNode;
}
}
//free VertexNode
free((*AG)->Vertices);
(*AG)->Vertices = NULL;
//free AGraph
(*AG)->VertexNum = 0;
(*AG)->ArcNum = 0;
free(*AG);
*AG = NULL;
Log("Destory Undirection Net Use AGraph : OK\n",Info);
return SuccessFlag;
}
六、Linux环境编译测试
[gbase@czg2 Graph]$ make
gcc -Wall -Wextra -g ../Log/Log.c ../PublicFunction/PublicFunction.c ../PublicFunction/SqQueue/SqQueue.c Graph.c main.c -o TestGraph -I ../Log/ -I ../PublicFunction/ -I ../Select/ -I ../PublicFunction/SqQueue/
[gbase@czg2 Graph]$ ./TestGraph
[2023-5]--[ Info ]--Create Net Data : OK
[2023-5]--[ Info ]--Create Undirection Net Use AMGraph : OK
[2023-5]--[ Debug ]--Printf AMGraph :
VertexArray : [A ,B ,C ,D ,E ]
ArcArray :
[32767 ,20 ,30 ,10 ,32767 ]
[20 ,32767 ,32767 ,32767 ,50 ]
[30 ,32767 ,32767 ,10 ,32767 ]
[10 ,32767 ,10 ,32767 ,20 ]
[32767 ,50 ,32767 ,20 ,32767 ]
CurVertexNum : 5
CurArcNum : 12
[2023-5]--[ Info ]--Create Undirection Net Use AGraph : OK
[2023-5]--[ Debug ]--Printf AGraph :
A : [ (2, 30, 0xe078b0),(1, 20, 0xe07870),(3, 10, (nil))]
B : [ (4, 50, 0xe078d0),(0, 20, (nil))]
C : [ (3, 10, 0xe07910),(0, 30, (nil))]
D : [ (4, 20, 0xe07950),(2, 10, 0xe07890),(0, 10, (nil))]
E : [ (3, 20, 0xe07990),(1, 50, (nil))]
VertexNum : 5
ArcNum : 12