用数组实现邻接表

2023-11-01

用数组实现邻接表

转载自:http://blog.csdn.net/wang2534499/article/details/47750321【坐在马桶上看算法】算法8:巧妙的邻接表(数组实现)

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

  
  
  1. 4 5   
  2. 1 4 9   
  3. 4 3 8   
  4. 1 2 5   
  5. 2 4 6   
  6. 1 3 7 

091650e0f00oqrcjcfnq93.png

第一行两个整数n m。n表示顶点个数(顶点编号为1~n),m表示边的条数。接下来m行表示,每行有3个数x y z,表示顶点x到顶点y的边的权值为z。下图就是一种使用链表来实现邻接表的方法。

091650gyll6hbqbjyxls8s.png

上面这种实现方法为图中的每一个顶点(左边部分)都建立了一个单链表(右边部分)。这样我们就可以通过遍历每个顶点的链表,从而得到该顶点所有的边了。使用链表来实现邻接表对于痛恨指针的的朋友来说,这简直就是噩梦。这里我将为大家介绍另一种使用数组来实现的邻接表,这是一种在实际应用中非常容易实现的方法。这种方法为每个顶点i(i从1~n)也都保存了一个类似“链表”的东西,里面保存的是从顶点i出发的所有的边,具体如下。

首先我们按照读入的顺序为每一条边进行编号(1~m)。比如第一条边“1 4 9”的编号就是1,“1 3 7”这条边的编号是5。

这里用u、v和w三个数组用来记录每条边的具体信息,即u[i]、v[i]和w[i]表示第i条边是从第u[i]号顶点到v[i]号顶点(u[i]àv[i]),且权值为w[i]。

091650h35zq3wgx30x3oe3.png

再用一个first数组来存储每个顶点其中一条边的编号。以便待会我们来枚举每个顶点所有的边(你可能会问:存储其中一条边的编号就可以了?不可能吧,每个顶点都需要存储其所有边的编号才行吧!甭着急,继续往下看)。比如1号顶点有一条边是 “1 4 9”(该条边的编号是1),那么就将first[1]的值设为1。如果某个顶点i没有以该顶点为起始点的边,则将first[i]的值设为-1。现在我们来看看具体如何操作,初始状态如下。

091650zw3988qpj5iljj8g.png

咦?上图中怎么多了一个next数组,有什么作用呢?不着急,待会再解释,现在先读入第一条边“1 4 9”。

读入第1条边(1 4 9),将这条边的信息存储到u[1]、v[1]和w[1]中。同时为这条边赋予一个编号,因为这条边是最先读入的,存储在u、v和w数组下标为1的单元格中,因此编号就是1。这条边的起始点是1号顶点,因此将first[1]的值设为1。

另外这条“编号为1的边”是以1号顶点(即u[1])为起始点的第一条边,所以要将next[1]的值设为-1。也就是说,如果当前这条“编号为i的边”,是我们发现的以u[i]为起始点的第一条边,就将next[i]的值设为-1(貌似的这个next数组很神秘啊⊙_⊙)。

091651kwo5g0aycy07wfwd.png

读入第2条边(4 3 8),将这条边的信息存储到u[2]、v[2]和w[2]中,这条边的编号为2。这条边的起始顶点是4号顶点,因此将first[4]的值设为2。另外这条“编号为2的边”是我们发现以4号顶点为起始点的第一条边,所以将next[2]的值设为-1。

091651fkswcj34c05k8w4k.png

读入第3条边(1 2 5),将这条边的信息存储到u[3]、v[3]和w[3]中,这条边的编号为3,起始顶点是1号顶点。我们发现1号顶点已经有一条“编号为1 的边”了,如果此时将first[1]的值设为3,那“编号为1的边”岂不是就丢失了?我有办法,此时只需将next[3]的值设为1即可。现在你知道next数组是用来做什么的吧。next[i]存储的是“编号为i的边”的“前一条边”的编号。

091651df28foy9ct7fl7qf.png

读入第4条边(2 4 6),将这条边的信息存储到u[4]、v[4]和w[4]中,这条边的编号为4,起始顶点是2号顶点,因此将first[2]的值设为4。另外这条“编号为4的边”是我们发现以2号顶点为起始点的第一条边,所以将next[4]的值设为-1。

091652vf4eg69f5zfsese9.png

读入第5条边(1 3 7),将这条边的信息存储到u[5]、v[5]和w[5]中,这条边的编号为5,起始顶点又是1号顶点。此时需要将first[1]的值设为5,并将next[5]的值改为3。

091652li6mzammza242tmp.png

此时,如果我们想遍历1号顶点的每一条边就很简单了。1号顶点的其中一条边的编号存储在first[1]中。其余的边则可以通过next数组寻找到。请看下图。

091652rtjh5qe2211eee58.png

细心的同学会发现,此时遍历边某个顶点边的时候的遍历顺序正好与读入时候的顺序相反。因为在为每个顶点插入边的时候都直接插入“链表”的首部而不是尾部。不过这并不会产生任何问题,这正是这种方法的其妙之处。

创建邻接表的代码如下。

  
  
  1. int n,m,i;   
  2. //u、v和w的数组大小要根据实际情况来设置,要比m的最大值要大1   
  3. int u[6],v[6],w[6];   
  4. //first和next的数组大小要根据实际情况来设置,要比n的最大值要大1   
  5. int first[5],next[5];   
  6. scanf("%d %d",&n,&m);   
  7. //初始化first数组下标1~n的值为-1,表示1~n顶点暂时都没有边   
  8. for(i=1;i<=n;i++)   
  9.     first[i]=-1;   
  10. for(i=1;i<=m;i++)   
  11. {   
  12.     scanf("%d %d %d",&u[i],&v[i],&w[i]);//读入每一条边   
  13.     //下面两句是关键啦   
  14.     next[i]=first[u[i]];   
  15.     first[u[i]]=i;   

接下来如何遍历每一条边呢?我们之前说过其实first数组存储的就是每个顶点i(i从1~n)的第一条边。比如1号顶点的第一条边是编号为5的边(1 3 7),2号顶点的第一条边是编号为4的边(2 4 6),3号顶点没有出向边,4号顶点的第一条边是编号为2的边(2 4 6)。那么如何遍历1号顶点的每一条边呢?也很简单。请看下图:

遍历1号顶点所有边的代码如下。

  
  
  1. k=first[1];// 1号顶点其中的一条边的编号(其实也是最后读入的边)   
  2. while(k!=-1//其余的边都可以在next数组中依次找到   
  3. {   
  4.     printf("%d %d %d\n",u[k],v[k],w[k]);   
  5.     k=next[k];   

遍历每个顶点的所有边的代码如下。

  
  
  1. for(i=1;i<=n;i++)   
  2. {   
  3.     k=first[i];   
  4.     while(k!=-1)   
  5.     {   
  6.         printf("%d %d %d\n",u[k],v[k],w[k]);   
  7.         k=next[k];   
  8.     }   

可以发现使用邻接表来存储图的时间空间复杂度是O(M),遍历每一条边的时间复杂度是也是O(M)。如果一个图是稀疏图的话,M要远小于N2。因此稀疏图选用邻接表来存储要比邻接矩阵来存储要好很多。



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用数组实现邻接表 的相关文章

随机推荐

  • Linux下如何使用命令安装g++

    g 是 GNU 的 c 编译器 它可以用来编译 c 程序 那么如何用命令安装 g 呢 注意 以下操作均在centos 7 3 1611 版本进行 那么开始进行操作 首先切换到 root 用户 然后输入yum install gcc c 回车
  • Java 窗口透明化(无边框)

    这几天我在写一个记事本程序 为了方便记事本记事 我准备将记事本界面透明化 于是上网查了一下资料 然后将透明化界面做了出来 可是在做的过程中我还是遇到了一些小小的问题 所以就把它记录了下来 问题1 奇怪的错误 访问限制 Access rest
  • 考研数学自整理,弥补知识漏洞(强化、冲刺)

    本次分享的是博主在考研时整理的最后一份数学知识 也是上考场前对知识最后的强化 因为博主是二战上岸 第一年考数三 第二年考数一 虽然这份笔记总结的内容不多 但这里浓缩了2020前历年数三 数的一真题与模拟题易错的考点和难点 链接 https
  • Java 正则表达式匹配规则

    表示捕获分组 会把每个分组里的匹配的值保存起来 使用 n n是一个数字 表示第n个捕获组的内容 表示非捕获分组 和捕获分组唯一的区别在于 非捕获分组匹配的值不会保存起来 0次或者多次 匹配前面的子表式零次或多次 1次或者多次 匹配前面的子表
  • 给一非空的单词列表,返回前 k 个出现次数最多的单词。旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现。 现在给出应该输入的一段文字、以及实际被输入的文字,请你列出肯定坏掉的那些键。

    Arrays sort是针对数组排顺序 也可以通过第二个参数指定比较规则 Collection sort是针对集合类排序 给一非空的单词列表 返回前 k 个出现次数最多的单词 旧键盘上坏了几个键 于是在敲一段文字的时候 对应的字符就不会出现
  • [蓝帽杯 2022 初赛]之Misc篇(NSSCTF)刷题记录(复现)

    NSSCTF Misc篇 蓝帽杯 2022 初赛 domainhacker 蓝帽杯 2022 初赛 domainhacker1 蓝帽杯 2022 初赛 domainhacker2 计算机取证 蓝帽杯 2022 初赛 计算机取证 1 蓝帽杯
  • Centos卸载gcc

    1 查看安装的gcc版本 y localhost Desktop rpm q gcc gcc 4 4 7 23 el6 x86 64 2 执行卸载命令 rpm e y localhost Desktop rpm e gcc 4 4 7 23
  • Algorithm Accumulation

    目标检测 DPM HOG SIFT 最优匹配 DTW HMM 优化入门 爬山算法 退火算法 遗传算法 多目标优化 ILP 复杂度 NP完全问题 这篇博客就用来积累学过的一些比较好的算法 现开始觉得算法真是一个宝啊 目标检测 DPM 参考博客
  • Oracle中5中分区介绍

    一 范围分区 就是根据数据库表中某一字段的值的范围来划分分区 例如 Sql代码 create table graderecord sno varchar2 10 sname varchar2 20 dormitory varchar2 3
  • Srpingboot注解:@ConditionalOnProperty的用法

    在之前我就一直有一个想法 就是有的bean我不想注入 比如说有的时候用的是A的bean 可能换了一个不同的系统可能要用B的bean 我可不可以自己去配置什么时候注入A什么时候注入B 而不是我都注入 用到那个用哪个 实际 Conditiona
  • Tomcat

    一 Web资源的分类 Web资源分为两类 静态资源和动态资源 静态资源 html css js mp4视频 jpg图片 txt等 动态资源 jsp页面 servlet程序等 二 常用的web服务器 Web服务器 也可以叫javaweb容器
  • 驱动开发 day4 9/13

    head h ifndef HEAD H define HEAD H typedef struct unsigned int MODER unsigned int OTYPER unsigned int OSPEEDR unsigned i
  • JAVA中类的public class与class的区别详解

    1 类的访问权限 为了控制某个类的访问权限 修饰词必须出现在关键字class之前 例如 public class Student 在编写类的时候可以使用两种方式定义类 A public class定义类 B class定义类 2 publi
  • 2021-11-3 内核态和核心态

    1 什么是内核态 当一个进程进行系统调用而陷入内核代码中时 就称程序陷入了内核态 否则就是用户态 用户程序运行在用户态而操作系统运行在内核态 用户态只能执行非特权指令 而内核态可以执行全部指令 内核态可以执行的指令比如 执行I O 设置时钟
  • 拓数派荣获 “2023 年杭州市企业高新技术研究开发中心” 认定

    近日 拓数派上榜由杭州市科学技术局公布的 2023 年杭州市企业高新技术研究开发中心名单 通过专业的技术研发 成果转化和高新技术产业化成绩 获得 杭州市拓数派数据计算企业高新技术研究开发中心 认定 图为 杭州市拓数派数据计算企业高新技术研究
  • Lattice库联合ModelSim仿真FIFO

    Lattice联合ModelSim仿真FIFO 前言 一 添加IP 二 库文件添加 一 方式一 添加器件库到ModelSim 二 方法二 直接添加器件库到Libray 和tb v在同一个目录下仿真 三 仿真 一 仿真文件 二 仿真结果 前言
  • 计算机进制存在的意义

    发布本文的实际想法 是否想过为什么要有进制的存在 如二进制 十进制等 存在即合理 通过思考 得出以下结论 希望能在计算机基础方面给你带来一定的帮助 进制的意义 二进制是计算机看得懂的语言 我们在电脑上所写的一切东西 都需要转成二进制让电脑执
  • ctfshow web4

    提示日志注入 文件包含 还有一个get方式的参数url 有nginx 那就试试它的默认日志目录 日志默认路径 1 apache Linux 日志默认路径 etc httpd logs access log var log httpd acc
  • Window下编译PJSIP,不编译Media模块

    Windows系统下使用VS2017进行编译PJSIP 下载地址 PJSIP的地址 https github com pjsip pjproject 在线clone太慢的话 可以直接在这里下载比clone会快很多 https github
  • 用数组实现邻接表

    用数组实现邻接表 转载自 http blog csdn net wang2534499 article details 47750321 坐在马桶上看算法 算法8 巧妙的邻接表 数组实现 之前我们介绍过图的邻接矩阵存储法 它的空间和时间复杂