图——数据结构 邻接表
前言:复习一下数据结构,今天主要是学习 图存储的思想 邻接表
邻接表是什么呢,这要从邻接表的具体实现说起:
首先我们给大家看一个邻接表的实例,
在这个邻接表的示意图中我们可以看出来,邻接表是由一个一个数组组成的,这个数组包含了所有的顶点,我们也可以叫他为顶点数组,也就是v1、v2、v3,而v1后面还跟着一个链表,表示着由v1出发的弧指向的节点,邻接表中所储存的信息也就是顶点之间相邻关系的信息,也就是弧的信息,这就是邻接表的由来;
也许你注意到了,v1后面的链表中节点的内容,正是v1连接的点在图中的位置,这个在图中的位置也许不太好理解,我们换一种说法,节点中的内容就是v1连接的点,在顶点数组中的索引,比如v2,在图中的位置就是1,同理v3是2;
好了,看了邻接表的示意图我们大概能了解邻接表是如何存储的了,如果用C语言来实现的话,我们需要三种结构体:
1.图结构体,包含一个顶点数组,还有图的基本信息,诸如节点数量,弧的数量
2.顶点结构体,包含顶点的信息,和这个顶点所连接的第一个节点的地址,还需要一个节点类型地址P,用来存放当前的信息,为什么需要这个地址我们将在后面构建邻接表的时候介绍
3.节点的结构体,包含顶点在图中的位置,下一个邻接节点的地址,如果是网的话还需要权值作为附加信息
下面我们来构建一个有向图的邻接表,构建一个邻接表需要这几个过程,首先我们需要知道图中有哪些顶点,哪几个顶点之间连成了边,这时候我们应该让用户输入图的定点数和边数,然后按照顶点数大小接收相应的顶点,接着我们开始构建邻接表,邻接表的构建从用户输入边开始,假设用户输入了 “a b” 代表这条边是由a指向b的,我们需要做的是查找这条边的起点,也就是a,在图中的位置(顶点数组的索引)然后将b连接上去,当我们准备将他连接上去的时候我们发现了一些状况,这个顶点已经连接了一个节点,和没有连接顶点,是两种不同的情况,所以我们应该在连接之前做一个 if 判断,我们先考虑没有连接顶点的情况,这时我们需要先创建节点的信息,然后让v1(a)顶点的第一个节点的地址指向我们刚才创建的节点,如果已经连接了节点,我们要做的仍然是创建节点,设置好新节点的信息,然后让最后一个节点的next指向新节点,这里我们就解释了P的作用,P就是用来找所谓的最后一个节点的;
如此如此,这番这番,我们就把构建一个邻接表的大致思路介绍完了,下面是C语言创建一个邻接表的具体代码,谨代表笔者当前思路,随着笔者更加深入的学习,一定会对下面的代码做出review,甚至还要使用其他诸如C++,java,JavaScript等其他热门高级语言实现;
这里我们一直提到的邻接节点也好,节点也好,在下一节十字链表中会有更加深入的理解(其实是弧节点,要不然为什么叫 “邻接“ 表呢,你品,你细品)
这份代码中,我用来标记一个顶点没有后续弧节点跟随的办法是把顶点中最后一个节点的值设置成这个顶点的地址,这是不兼容的,在十字链表中我是用了NULL进行判断,这里笔者想记录一下这个小点就不再做修改,如果了解C语言应该很容易可以看的懂
//图的邻接表储存验证,为了简单起见,我们使用有向图
#include<stdio.h>
#include<windows.h>
#define emm 100
//-------------------------------------------------------------------------------------------------------------
//弧节点结构体
typedef struct ArcNode{
int adjvex;
struct ArcNode *nextarc;
}ArcNode;
//顶点节点结构体
typedef struct VexNode{
char adjvex; //头节点内容
ArcNode *secendNode;
ArcNode *endNode;
}VexNode;
//顶点数组
VexNode adjList[emm];
//图的结构体
typedef struct ALGraph{
VexNode adjList[emm];
int vexNum;
int edgeNum;
}ALGraph;
//-------------------------------------------------------------------------------------------------------------
//在图中查找顶点在顶点数组中的索引
int LocateVex(ALGraph g,char v){
int i;
for(i=0;i<g.vexNum;i++){
if(g.adjList[i].adjvex==v){
return i;
}
}
return -1;
}
//-------------------------------------------------------------------------------------------------------------
//建立邻接表
void creatAdjList(ALGraph *g){
//建立节点数组
printf("请输入节点数和边数\n");
scanf("%d %d",&g->vexNum,&g->edgeNum);
getchar();
int i;//不知道哪个版本C语言不支持在for中声明变量了
printf("请输入节点,不要空格\n");
for(i=0;i<g->vexNum;i++){
scanf("%c",&g->adjList[i].adjvex);
g->adjList[i].secendNode=NULL;
g->adjList[i].endNode=&(g->adjList[i]);
}
getchar();//此时\n也被吸收了
//建立邻接表主要内容
char v1,v2;
ArcNode *temp;
printf("请输入对应的边\n");
int k=0;
for(k;k<g->edgeNum;k++){
scanf("%c %c",&v1,&v2);
getchar();
int m=LocateVex(*g,v1);
int n=LocateVex(*g,v2);
if(g->adjList[m].secendNode!=NULL){
temp=(ArcNode *)malloc(sizeof(ArcNode));
temp->adjvex=n; //索引
g->adjList[m].endNode->nextarc=temp;
g->adjList[m].endNode=temp;
}
else{
temp=(ArcNode *)malloc(sizeof(ArcNode));
temp->adjvex=n; //索引
g->adjList[m].secendNode=temp;
//接上这个节点之后,把最后的节点地址记录下来
g->adjList[m].endNode=temp;
}
}
return ;
}
int main(){
//声明一个图woc
ALGraph woc;
//建立邻接表
creatAdjList(&woc);
printf("%d\n",woc.vexNum);
//打印邻接表
ArcNode *show;
int i=0;
for(i;i<woc.vexNum;i++){
printf("%c ",woc.adjList[i].adjvex);
//不兼容的指针类型
show=&woc.adjList[i];
if(show==woc.adjList[i].endNode){
printf("\n");
continue;
}
show=woc.adjList[i].secendNode;
while(show!=woc.adjList[i].endNode){
printf("%c ",woc.adjList[show->adjvex].adjvex);
show=show->nextarc;
}
printf("%c\n",woc.adjList[show->adjvex].adjvex);
}
putchar('\n');
system("Pause");
return 0;
}
博客中引用的图片是拍的梁海英、王凤领主编的《数据结构(C语言版)》中的例子
这是本人原创的一篇学习笔记,转载请注明来源(抱拳)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现