【数据结构】【C语言】图
前言
本文为个人笔记。作者会不定时修改文章内容。
图存储结构用来存储具有“多对多”逻辑关系的数据。
1. 应知应会
1.1 顶点
图中存储的各个数据元素被称为顶点(不是节点)。
习惯用V[i]表示图中的顶点,且所有的顶点构成的集合通常用V表示,例如V={V1, V2, V3, V4}。
1.2 边或弧
- 连接顶点的没有方向的线叫做边,也叫做无向边
- 用(V1, V2)表示连通顶点V1和顶点V2的边
- 连接顶点的有方向的线叫做弧,也叫做有向边
- 用<V1, V2>表示连通顶点V1和顶点V2的弧
- 弧指向的顶点被称为终端点或弧头
- 弧远离的顶点被称为初始点或弧尾
1.3 图的组成
- 由一些顶点和一些连接顶点的线组成的
1.4 无向图和有向图
1.4.1 无向图 Undirected Graph
- 由一些顶点和一些连接顶点的无向边组成的
1.4.2 有向图 Directed Graph
- 由一些顶点和一些连接顶点的弧组成的。
入度
对于有向图中的一个顶点来说,弧头指向该顶点的弧的数量为该顶点的入度(InDegree,记为ID(V))
出度
对于有向图中的一个顶点来说,弧头远离该顶点的弧的数量为该顶点的出度(OutDegree,记为OD(V))
1.5 集合VR的含义
用VR表示图中所有顶点之间关系的集合
VR={V1, V2, V3, V4, V5}
1.6 路径和回路
无论是有向图还是无向图,从一个顶点到另一个顶点途径的所有顶点组成的序列(包含这两个顶点),称为一条路径。
如果路径中第一个顶点和最后一个顶点相同,则此路径称为回路,或称为环。
若路径中各顶点都不重复,此路径又被称为简单路径;
若回路中的顶点互不重复,此回路被称为简单回路。
图中一部分顶点和边构成的图,称为原图的子图。
1.7 权和网
在某些场景中,图中的每条边(弧)会被赋予一个实数来表示一定的含义,这种与边(或弧)向匹配的实数被称为权,而带权的图常称为网
1.8 图存储结构的分类
1.8.1 完全图
若图中各个顶点都与除自身外的其他顶点有关系,这样的无向图称为完全图
满足此条件的有向图则称为有向完全图。
- 具有n个顶点的完全图,图中边的数量为n(n-1)/2
- 对于具有n个顶点的有向完全图,图中弧的数量为n(n-1)
1.8.2 稀疏图和稠密图
- 稀疏图和稠密图是相对存在的
- 如果图中具有很少的边(或弧),此图就称为”稀疏图“,反之,此图称为”稠密图“
- 稀疏和稠密的判断条件是:e<nlogn,其中e表示边(或弧)的数量,n表示顶点的数量。
1.8.3 连通图
1.8.3.1 连通图
无向图中,如果任意两个顶点之间都能够连通,则称此无向图为连通图。
若无向图不是连通图,但图中存储的某个子图符合连通图的性质,则称为该子图为连通分量。
1.8.3.2 强连通图
有向图中,如果任意两个顶点之间,双向都连通,也就是至少含有一个通路,则称此有向图为强连通图。
如有向图不是强连通图,但其包含的连通子图具有强连通图的性质,则称该连通子图为强连通分量
1.8.3.3 生成树
对连通图进行遍历,过程中所经历的边和顶点的组合可看作是一颗普通树,通常称为生成树。
连通图中生成树的必需满足的条件
- 包含连通图中所有的顶点
- 任意两顶点之间有且只有一条通路
连通图的生成树具有这样的特性,即生成树中边的数量 = 顶点数 - 1
1.8.3.4 生成森林
生成树是对连通图来说的,而生成森林是对应非连通图来说
2. 图的顺序存储结构
使用数组存储图时,需要使用两个数组。
- 一个数组存放图中顶点本身的数据(一维数组)
- 另外一个数组用于存储各顶点之间的关系(二维数组)
2.1 图的结构
#define MAX_VERtEX_NUM 20 //顶点的最大个数
#define VRType int //顶点之间关系的变量类型,无权图,用0和1表示,有权图直接为权值
#define InfoType char //存储弧或者边的额外信息的指针
#define VertexType infoType //图中顶点的数据类型
typedef enum{DG, DN, UDG, UDN}GrapKind; //枚举图的4中类型,有向图,有向网、图,网
typedef struct
{
VRType adj; //无权图,用0和1表示是否相邻;对于带权图,直接为权值;
InfoType *info; //图或者边额外含有的信息指针
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];
typedef struct
{
VertexType vexs[MAX_VERtEX_NUM]; //存储图中顶点数据
AdjMatrix arcs; //二维数组,记录顶点之间的关系
int vexnum, arcnum; //记录图的顶点数和弧(边)数
GraphKind kind; //记录图的种类
}
2.2 图的顺序存储结构实现
#include<stdio.h>
#define MAX_VERtEX_NUM 20
#define VRType int
#define InfoType char
#define VertexType int
typedef enum{DG, DN, UDG, UDN}GraphKind;
typedef struct
{
VRType adj;
InfoType *info;
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];
typedef struct
{
VertexType vexs[MAX_VERtEX_NUM];
AdjMatrix arcs;
int vexnum, arcnum;
GrapKind kind;
}MGraph;
int LocatVex(MGraph *G, VertexType v)
{
int i = 0;
for(; i < G->vexnum; i++)
{
if(G->vexs[i] == v)
{
break;
}
}
if(i >= G->vexnum)
{
printf("No such vertex.\n");
return -1;
}
return i;
}
//构造有向图
void CreatDG(MGraph *G)
{
scanf("%d, %d", &(G->vexnum), &(G->arcnum));
for(int i = 0; i <= G->vexnum; i++)
{
G->vexnum = i + 1; //输入顶点数据
}
//初始化二维矩阵,全部归0,指针指向NULL
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;
//输入弧头和弧尾
scanf("%d, %d", &v1, &v2);
//确定顶点位置
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 = 1;
}
}
//构造无向图
void CreatUDM(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;
scanf("%d, %d", &v1, &v2);
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 = 1;
G->arcs[m][n].adj = 1;
}
}
//构造有向网
void CreatDN(MGraph *G)
{
//输入顶点vexnum, 弧(或边)vexnum
scanf("%d, %d", &(G->vexnum), &(G->vexnum));
//输入各顶点的数据
for(int i = 0; i <= G->vexnum; i++)
{
//手动输入
//scanf("%d", &(G->vexs[i]));
//这里赋值i+1
G->vexs[i] = i + 1;
}
//arcs[i][j].adj赋初值0,也就是将权值置为0
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;
}
}
//构造无向网
void CreatUDN(MGraph *G)
{
//输入顶点vexnum,弧或边arcnum
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 CreateGraph(MGraph *G)
{
//选择图的类型
scanf("%d", &(G->kind));
//根据所选类型,调用不同的函数实现构造图的功能
switch(G->kind)
{
case DG:
return CreateDG(G);
break;
case DN:
return CreateDN(G);
break;
case UDG:
return GreateUDG(G);
break;
case UDM:
return CreateUDM(G);
break;
default:
break;
}
}
void PrintGrapth(MGrap G)
{
for(int i = 0; i < G.vexnum; i++)
{
for(int j = 0; j < G.vexnum; j++)
{
printf("%d", G.arcs[i][j].adj);
}
printf("\n");
}
}
int main()
{
MGraph G; //建立一个图的变量
CreateGraph(&G); //调用创建函数,传入地址参数
PrintGraph(G); //输出图的二阶矩阵;
return 0;
}
3. 图的链表存储
图更多的是采用链表存储,具体的实现方法分别是邻接表,邻接多重表和十字链表
3.1 图的邻接表存储结构
即适用存储无向图,也适用于存储有向图
- 邻接点,在图中,如果两个点相互连通,即通过其中一个顶点,可以直接找到另一个顶点,则称它们互为邻接点
- 邻接,指图中顶点之间有边或者弧的存在
实现方式
给图中各个顶点独自建立一个链表,用链表中的节点存储该顶点,用链表中其他节点存储各自的邻接点。
3.1.1 结构
#define MAX_VERTEX_NUM 20//最大顶点个数
#define VertexType int//顶点数据的类型
#define InfoType int//图中弧或者边包含的信息的类型
typedef struct ArcNode{
int adjvex;//邻接点在数组中的位置下标
struct ArcNode * nextarc;//指向下一个邻接点的指针
InfoType * info;//信息域
}ArcNode;
typedef struct VNode{
VertexType data;//顶点的数据域
ArcNode * firstarc;//指向邻接点的指针
}VNode,AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组
typedef struct {
AdjList vertices;//图中顶点的数组
int vexnum,arcnum;//记录图中顶点数和边或弧数
int kind;//记录图的种类
}ALGraph;
3.1.2 实现
3.2 图的十字链表存储结构
3.2.1 结构
3.2.2 实现
3.3 图的邻接多重表存储结构
3.3.1 结构
3.3.2 实现
结语
感谢阅读,欢迎各位读者在评论区留言。如果文章对你有帮助,欢迎点赞收藏。