文章目录

  • 一、定义
  • 二、结构
  • 三、常用操作
  • 四、测试
  • 结语
  • 附录

一、定义

图的邻接表是一种顺序与链式存储相结合的存储方式。下面给出一个示例,以便大家能够理解邻接表这种存储方式:
        无向图G1


        用邻接表来存储G1


        每一个顶点所在结点都是之后链表的头结点,之后的链表结点存放从头结点所存顶点能够直接到达顶点的位置下标,如顶点A能够直接到达D,B两个顶点,D的顶点存放在3位置,B顶点存放在1位置,所以A之后的链表结点存放的值为3和1
        有向图G2

        用邻接表来存储有向图G2

        可见无向图和有向图都可以用邻接表来表示,只需将无向图中连接两个顶点的边看成是有向图中两条相互指向的边,就可以将无向图转换成有向图进行处理。

二、结构

结构图

代码描述

//顶点默认元素个数
#define Default_Vertex_Size 10
#define T  char //顶点元素类型
//边结构
typedef struct Edge
{int dest;  //存放顶点下标struct Edge *link;//连接下一个顶点
}Edge;
//顶点结构
typedef struct Vertex
{T data;//顶点数据Edge *adj;//指向边结构指针
}Vertex;
//邻接表
typedef struct GraphLnk
{int MaxVertices; //最大顶点个数int NumVertices; //实际顶点个数int NumEdges;  //边的条数Vertex *NodeTable; //指向顶点结构表
}GraphLnk;

三、常用操作

下面给出存储图的邻接表的常用操作代码实现,无向图和有向图的邻接表操作代码实现上基本一致,只有在插入边操作和删除边操作中存在一些差异。

初始化

//初始化
void InitGraph(GraphLnk *g)
{g->MaxVertices = Default_Vertex_Size;//初始化最大顶点个数g->NumEdges = g->NumVertices = 0;//初始化实际顶点个数和边条数//为顶点结构表开辟空间g->NodeTable = (Vertex*)malloc(sizeof(Vertex) * g->MaxVertices);assert(g->NodeTable != NULL);for(int i=0; i<g->MaxVertices; ++i)//顶点结构表的初始化{g->NodeTable[i].adj = NULL;}
}

获取顶点位置

//获取顶点位置
int  GetVertexPos(GraphLnk *g, T v)
{for(int i=0; i<g->NumVertices; ++i){if(g->NodeTable[i].data == v)//判断是否找到return i;}return -1;
}

打印图

//打印图
void ShowGraph(GraphLnk *g)
{Edge *p;for(int i=0; i<g->NumVertices; ++i)//对顶点进行遍历{printf("%d %c:>",i,g->NodeTable[i].data);//输出顶点p = g->NodeTable[i].adj;//指向边结构while(p != NULL)//打印相连的顶点{printf("%d-->",p->dest);p = p->link;}printf("Nul.\n");}printf("\n");
}

插入顶点

//插入顶点
void InsertVertex(GraphLnk *g, T v)
{//判断顶点表是否已满if(g->NumVertices >= g->MaxVertices)return;g->NodeTable[g->NumVertices++].data = v;//插入
}

插入边(无向图)

//插入边:在顶点vertex1和vertex2之间插入一条边
void InsertEdge(GraphLnk *g, T vertex1, T vertex2)
{int v1 = GetVertexPos(g,vertex1);//获取vertex1的位置int v2 = GetVertexPos(g,vertex2);//获取vertex2的位置if(v1==-1 || v2==-1)return;Edge *s;//无向图是双向的插入要两次//插入V1 --> V2的边 头插法插入s = (Edge *)malloc(sizeof(Edge));assert(s != NULL);s->dest = v2;s->link = g->NodeTable[v1].adj;g->NodeTable[v1].adj = s;//插入V2 --> V1的边 头插法插入s = (Edge *)malloc(sizeof(Edge));assert(s != NULL);s->dest = v1;s->link = g->NodeTable[v2].adj;g->NodeTable[v2].adj = s;g->NumEdges++;
}

插入边(有向图)

//插入边:在顶点vertex1和vertex2之间插入一条边
void InsertEdge(GraphLnk *g, T vertex1, T vertex2)
{int v1 = GetVertexPos(g,vertex1); //获取vertex1的位置int v2 = GetVertexPos(g,vertex2); //获取vertex2的位置if(v1==-1 || v2==-1)return;Edge *s;//插入V1 --> V2的边 头插法插入s = (Edge *)malloc(sizeof(Edge));assert(s != NULL);s->dest = v2;s->link = g->NodeTable[v1].adj;g->NodeTable[v1].adj = s;g->NumEdges++;
}

删除边(无向图)

//删除一条边:删除顶点vertex1和顶点vertex2之间的边
void RemoveEdge(GraphLnk *g, T vertex1, T vertex2)
{int v1 = GetVertexPos(g,vertex1);//获取v1所在位置int v2 = GetVertexPos(g,vertex2);//获取顶点v2所在位置if(v1==-1 || v2==-1)return;Edge *q = NULL;Edge *p;//无向图是双向的,所以需要删除两边相对的边//删除v1 -- > v2的边p = g->NodeTable[v1].adj;while(p != NULL && p->dest != v2){//从v1后面的链表中查找v2顶点,其中q指向v2顶点的前驱,p指向v2顶点q = p;p = p->link;}if(p == NULL)return;if(q == NULL)//判断找到的结点是否是链表内的第一个结点{//是  头删g->NodeTable[v1].adj = p->link;//头删}else{//不是 直接删除q->link = p->link;}free(p); //释放空间//删除v2 --> v1的边q = NULL;p = g->NodeTable[v2].adj;while(p->dest != v1){//从v2后面的链表中查找v1顶点,其中q指向v1顶点的前驱,p指向v1顶点q = p;p = p->link;}if(q==NULL)//判断找到的结点是否是链表内的第一个结点{//是  头删g->NodeTable[v2].adj = p->link;}else{//不是 直接删除q->link = p->link;}free(p); //释放空间g->NumEdges--; //边数减一
}

删除边(有向图)

//删除一条边:删除顶点vertex1和顶点vertex2之间的边
void RemoveEdge(GraphLnk *g, T vertex1, T vertex2)
{int v1 = GetVertexPos(g,vertex1); //获取v1所在位置int v2 = GetVertexPos(g,vertex2); //获取顶点v2所在位置if(v1==-1 || v2==-1)return;Edge *q = NULL;Edge *p;//删除v1 -- > v2的边p = g->NodeTable[v1].adj;while(p != NULL && p->dest != v2){//从v1后面的链表中查找v2顶点,其中q指向v2顶点的前驱,p指向v2顶点q = p;p = p->link;}if(p == NULL)return;if(q == NULL)//判断找到的顶点是否是链表内的第一个顶点{//是  头删g->NodeTable[v1].adj = p->link; //头删}else{//不是 直接删除q->link = p->link;}free(p);  //释放空间g->NumEdges--;
}

删除顶点

//删除顶点
void RemoveVertex(GraphLnk *g, T vertex)
{int v = GetVertexPos(g,vertex);//获取顶点vertex的位置if(v == -1)return;//删除顶点所相连的边Edge *p = g->NodeTable[v].adj;//获取与顶点vertex相连的链表int k;Edge *t = NULL;//t是s的前驱Edge *s;while(p!=NULL){k = p->dest;//获取与v相连的顶点ks = g->NodeTable[k].adj;//获取与k连接的链表while(s!=NULL && s->dest!=v)//从该链表中查找与v连接的结点{t = s;s = s->link;}if(s!=NULL)//判断是否找到{//找到if(t==NULL)//判断是否是第一个结点{//是  头删g->NodeTable[k].adj = s->link;}else{//否 直接删除t->link = s->link;}free(s);//释放空间}//释放与v连接链表中的k结点g->NodeTable[v].adj = p->link;free(p);p = g->NodeTable[v].adj;}g->NumVertices--;//顶点数减一//拿最后一个顶点的值覆盖要删除的顶点g->NodeTable[v].data = g->NodeTable[g->NumVertices].data;g->NodeTable[v].adj = g->NodeTable[g->NumVertices].adj;//调整链表内结点的指向:将原来标明到最后一个顶点的结点,标明到最后一个结点更改后的位置s = g->NodeTable[v].adj;//指向原来与最后一个顶点连接的链表while(s != NULL)//对链表进行搜索,看哪些顶点与原来最后一个顶点相连{k = s->dest;//获取相连的顶点位置p = g->NodeTable[k].adj;//找到与相邻顶点连接的链表while(p != NULL)//对该链表进行搜索{if(p->dest == g->NumVertices)//判断是否找到指向最后一个顶点的结点{//找到p->dest = v;//更换指向,指向最后一个顶点更新后的位置break;}p = p->link;//没找到,则继续查找下一个}s = s->link;//进入下一个结点,准备下一个顶点链表结点的替换}
}

获取第一个邻接顶点

//获取第一个邻接顶点
int  GetFirstNeighbor(GraphLnk *g, T vertex)
{int v = GetVertexPos(g,vertex);//获取顶点vertex的位置if(v == -1)return -1;//从vertex所连接链表的第一个结点处取得第一个邻接顶点Edge *p = g->NodeTable[v].adj;if(p != NULL)return p->dest;return -1;
}

获取邻接顶点的下一个顶点

//获取邻接顶点的下一个顶点:获取顶点vertex1的邻接顶点,该邻接顶点在vertex1的邻接顶点vertex2的下一个
int  GetNextNeighbor(GraphLnk *g,  T vertex1, T vertex2)
{int v1 = GetVertexPos(g,vertex1);//获取vertex1的位置int v2 = GetVertexPos(g,vertex2);//获取vertex2的位置if(v1==-1 || v2==-1)return -1;Edge *p = g->NodeTable[v1].adj;//获取与顶点vertex1相连的链表while(p != NULL && p->dest != v2)//从链表中查找到指向vertex2顶点的结点位置p = p->link;if(p!=NULL && p->link!=NULL)//判断是否找到return p->link->dest;//找到,那么它的下一个结点就是所求的邻接顶点return -1;
}

销毁图

//销毁图
void DestroyGraph(GraphLnk *g)
{Edge *p;//释放每一个顶点指向的链表空间for(int i=0; i<g->NumVertices; ++i){p = g->NodeTable[i].adj;while(p != NULL){g->NodeTable[i].adj = p->link;free(p);p = g->NodeTable[i].adj;}}//释放存放顶点的空间free(g->NodeTable);g->NodeTable = NULL;g->MaxVertices = g->NumEdges = g->NumVertices = 0;
}

四、测试

无向图测试

#include"GraphLnk.h"void main()
{//建立无向图GraphLnk gl;InitGraph(&gl);//插入顶点InsertVertex(&gl,'A');InsertVertex(&gl,'B');InsertVertex(&gl,'C');InsertVertex(&gl,'D');InsertVertex(&gl,'E');//插入边InsertEdge(&gl,'A','B');InsertEdge(&gl,'A','D');InsertEdge(&gl,'B','C');InsertEdge(&gl,'B','E');InsertEdge(&gl,'C','D');InsertEdge(&gl,'C','E');ShowGraph(&gl);DestroyGraph(&gl);
}

运行结果

有向图测试

#include"GraphLnk.h"void main()
{//建立有向图GraphLnk gl;InitGraph(&gl);//插入顶点InsertVertex(&gl,'A');InsertVertex(&gl,'B');InsertVertex(&gl,'C');InsertVertex(&gl,'D');InsertVertex(&gl,'E');InsertVertex(&gl,'F');//插入边InsertEdge(&gl,'A','B');InsertEdge(&gl,'A','C');InsertEdge(&gl,'A','D');InsertEdge(&gl,'C','B');InsertEdge(&gl,'C','E');InsertEdge(&gl,'D','E');InsertEdge(&gl,'F','D');InsertEdge(&gl,'F','E');//显示图结构ShowGraph(&gl);//销毁图DestroyGraph(&gl);
}

运行结果

结语

对图的邻接表的介绍就到这里啦,希望这篇文章能给予你一些帮助,感谢各位人才的:点赞、收藏和评论,我们下次见。

附录

测试代码:图之邻接表详解(C语言版)

图之邻接表详解(C语言版)相关推荐

  1. 数据结构殷人昆电子版百度云资源_数据结构精讲与习题详解(C语言版第2版清华大学计算机系列教材)...

    导语 内容提要 殷人昆编著的<数据结构精讲与习题详解(C语言版第2版清华大学计算机系列教材)>是清华大学出版社出版的<数据结构(C语言版)>(第2版)的配套教材,对" ...

  2. 图的存储结构之邻接表(详解)

    之前我们介绍过图的邻接矩阵存储法,它的空间和时间复杂度都是N2,现在我来介绍另外一种存储图的方法:邻接表,这样空间和时间复杂度就都是M.对于稀疏图来说,M要远远小于N2.先上数据,如下. 1 2 3 ...

  3. 数据结构图之邻接表详解

    图数据结构 两种表示方式 邻接矩阵 优点:简单直观:方便查找任一顶点的所有邻接点:方便计算顶点的度:方便判断任意两个顶点是否存在边: 缺点:常采用二维数组稀疏存储,相对浪费空间:查找所有边相对耗时: ...

  4. 【八大排序详解~C语言版】直接插入排序-希尔排序- 直接选择排序-堆排序-冒泡排序-快速排序-归并排序-计数排序

    八大排序 1.直接插入排序 2.希尔排序 3.直接选择排序 直接选择排序改进 4.堆排序 1.建堆 2.利用堆删除思想来进行排序 5.冒泡排序 6.快速排序 递归实现 非递归实现 7.归并排序 递归实 ...

  5. mysql 邻接表_图的邻接表存储结构详解

    通常,图更多的是采用链表存储,具体的存储方法有 3 种,分别是邻接表.邻接多重表和十字链表. 本节先讲解图的邻接表存储法.邻接表既适用于存储无向图,也适用于存储有向图. 在具体讲解邻接表存储图的实现方 ...

  6. 数据结构(C语言版) 第 六 章 图 知识梳理 + 习题详解

    目录 一. 图的基本定义和术语 一.图的基本概念 1.度 2.连通 (1)连通图 (2)强连通/强连通图 3.回路 4.完全图 二.图的三种存储结构 1.邻接矩阵表示法 2.邻接表(链式)表示法 3. ...

  7. c语言邻接表的构建_c语言数据结构--图的邻接矩阵和邻接表操作的基本操作

    #include #include #include #define MAX 100 typedef char DataType; typedef int VectorRelationType; ty ...

  8. 数据结构 图的邻接表和邻接矩阵实现———c语言

    图的邻接矩阵实现 逻辑结构分为两部分:V和E集合.因此,用一个一维数组存放图中所有顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵.邻接矩阵又分为有向图邻接矩阵和无向图 ...

  9. 判断数组中某个元素除自身外是否和其他数据不同_算法工程师要懂的3种算法数据结构:线性表详解...

    算法思想有很多,业界公认的常用算法思想有8种,分别是枚举.递推.递归.分治.贪心.试探法.动态迭代和模拟.当然8种只是一个大概的划分,是一个"仁者见仁.智者见智"的问题. 其实这些 ...

最新文章

  1. 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)
  2. 这台计算机怎么磁盘清理,电脑硬盘满了怎么清理(教你3招彻底清理内存,瞬间多出几十个G)...
  3. 北斗导航 | 惯性导航之基于Matlab的IMU与GPS融合(附源代码)
  4. 协议crc计算_从零了解modbus协议 第三篇
  5. 理解并使用ASP.NET的高级配置
  6. SAP CDS view的文本格式的源代码是如何被ABAP后台解析的
  7. mysql 密码长度约束_MySQL简单操作【1、在cmd下MySQL的运行及简单增删改查】
  8. Win7系统中用anaconda配置tensorflow运行环境
  9. SQL基础E-R图画法(一)
  10. 商城文档、商城原型、全局说明、o2o商城、汽车商城、业务逻辑、商城架构图、流程图、版本规划、需求说明、活动、预约、会员、prd、交互说明、页面说明、显示规则、字段说明、操作规则、数据来源、需求规划
  11. 【编程】常见概念的理解 —— inplace、vanity url、vanilla(code/software)、编译、链接、build、(delegate、proxy)
  12. 网络流媒体(七)———PTSP
  13. No serializer found for class com.yumoxuan.domain.Store and no properties discovered to create BeanS
  14. linux内核如何支持多核cpu,现在的多核CPU,Linux操作系统是否能够实现单个进程(多线程)的多核调度(跨CPU核心调度)?...
  15. 充满正能量阳光活的生日祝福语
  16. 有些段子,外行人根本看不懂,只有程序员看了会狂笑不止
  17. 百度AI图像处理—人体分析(人像切割—AI抠图)调用教程(基于Python3-附Demo)
  18. 物联网教育现状和前景
  19. linux操作系统基础北京邮电大学出版社,Linux操作系统与实训教程
  20. JAVA 基础——学习

热门文章

  1. 自建小米插座服务器,如何造出39元的智能插座?——小米智能插座拆解
  2. 用户注册场景-手机短信验证解决
  3. 我去!企业微信BUG还真不少 大象盯盯成熟值得信任
  4. python爬虫可以爬什么山好_学技树-Python爬虫零基础到爬啥都行
  5. 3Dmax』打造沐浴用的金属软管
  6. [yotroy.cool]给博客配置botui添加对话交互框架-以halo博客为例
  7. java资源吧_哔哩哔哩有好的Java学习资源吗?
  8. 从一台虚拟机中启用一个并口设备
  9. 华创期货:新视角看金融期货与股票
  10. 应用之星,屌丝逆袭之APP开发神器