深度理解链式前向星

深度理解链式前向星我们首先来看一下什么是前向星 前向星是一种特殊的边集数组 我们把边集数组中的每一条边按照起点从小到大排序 如果起点相同就按照终点从小到大排序 并记录下以某个点为起点的所有边在数组中的起始位置和存储长度 那么前向星就构造好了 用 len i 来记录所有以 i 为起点的边在数组中的存储长度 用 head i 记录以 i 为边集在数组中的第一个存储位置 那么对于下图

我们首先来看一下什么是前向星.


前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,

并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了.


用len[i]来记录所有以i为起点的边在数组中的存储长度.

用head[i]记录以i为边集在数组中的第一个存储位置.


那么对于下图:


深度理解链式前向星




我们输入边的顺序为:


1 2

2 3

3 4

1 3

4 1

1 5

4 5


那么排完序后就得到:


编号:     1      2      3      4      5      6      7

起点u:    1      1      1      2      3      4      4

终点v:    2      3      5      3      4      1      5


得到:


head[1] = 1    len[1] = 3

head[2] = 4    len[2] = 1

head[3] = 5    len[3] = 1

head[4] = 6    len[4] = 2


但是利用前向星会有排序操作,如果用快排时间至少为O(nlog(n))



如果用链式前向星,就可以避免排序.


我们建立边结构体为:


struct Edge

{

     int next;

     int to;

     int w;

};


其中edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置,edge[i].w为边权值.


另外还有一个数组head[],它是用来表示以i为起点的第一条边存储的位置,实际上你会发现这里的第一条边存储的位置其实

在以i为起点的所有边的最后输入的那个编号.


head[]数组一般初始化为-1,对于加边的add函数是这样的:


void add(int u,int v,int w) { edge[cnt].w = w; edge[cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt++; }

初始化cnt = 0,这样,现在我们还是按照上面的图和输入来模拟一下:


edge[0].to = 2;     edge[0].next = -1;      head[1] = 0;

edge[1].to = 3;     edge[1].next = -1;      head[2] = 1;

edge[2].to = 4;     edge[2],next = -1;      head[3] = 2;

edge[3].to = 3;     edge[3].next = 0;       head[1] = 3;

edge[4].to = 1;     edge[4].next = -1;      head[4] = 4;

edge[5].to = 5;     edge[5].next = 3;       head[1] = 5;

edge[6].to = 5;     edge[6].next = 4;       head[4] = 6;


很明显,head[i]保存的是以i为起点的所有边中编号最大的那个,而把这个当作顶点i的第一条起始边的位置.


这样在遍历时是倒着遍历的,也就是说与输入顺序是相反的,不过这样不影响结果的正确性.

比如以上图为例,以节点1为起点的边有3条,它们的编号分别是0,3,5   而head[1] = 5


我们在遍历以u节点为起始位置的所有边的时候是这样的:


for(int i=head[u];~i;i=edge[i].next)


那么就是说先遍历编号为5的边,也就是head[1],然后就是edge[5].next,也就是编号3的边,然后继续edge[3].next,也

就是编号0的边,可以看出是逆序的.

 


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201913.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午8:19
下一篇 2026年3月20日 上午8:19


相关推荐

  • 安全且高效!360推出国内首个“安全龙虾”智能体

    安全且高效!360推出国内首个“安全龙虾”智能体

    2026年3月16日
    3
  • Spring 常用注解

    Spring 常用注解Spring 常用注解 Component 任何层 Controller Service Repository dao 用于实例化对象 Autowired 对象属性的依赖注入 Qualifier 要和 Autowired 联合使用 代表在按照类型匹配的基础上 再按照名称匹配 Resource 按照属性名称依赖注入 ComponentSca 组件扫描 Configuratio 被此注解标注的类 会被 Spring 认为是配置类 Spring 在启动的时候会自动扫描并加载所有配置类

    2026年3月16日
    1
  • java的实例变量_JAVA语言中的实例变量

    java的实例变量_JAVA语言中的实例变量JAVA 语言中的实例变量一个 Java 程序可以认为是一系列对象的集合 而这些对象通过调用彼此的方法来协同工作 下面是 JAVA 语言实例变量的介绍 欢迎参考 每个对象都有独特的实例变量 对象的状态由这些实例变量的值决定 java 第一个程序实例 publicclassH publicstatic String args System out print

    2025年11月10日
    4
  • 我的世界怎么设置传送点指令_我的世界手机版领地指令

    我的世界怎么设置传送点指令_我的世界手机版领地指令今天小编为玩家们带来了我的世界服务器领地指令_我的世界地皮指令大全,希望对玩家们有所帮助,还不了解的玩家快来看看吧。圈地指令用木棍(各个服务器不一样,绝大部分默认是木锄)左击一个点,右击一个点(两点内为你想圈的长宽高,对角,一个高点,一个低点。)然后输查询大小,在输入创建领地。查询区域大小/resselectsize创建领地/rescreate名字移除领地/resremove名字领地转赠/resg…

    2025年11月27日
    4
  • HTML做跳转另一个页面链接,html中如何链接到另一个页面

    HTML做跳转另一个页面链接,html中如何链接到另一个页面如何将一个 html 页面中嵌入另一个 html 页面将一个 html 页面中嵌入另一个 html 页面需要使用到 iframe 标签 iframe 标签用法 scrolling 禁止鼠标滑动 frameborder 嵌套页面边框 leftmargin 左边距 topmargin 上边距扩展资料 嵌入页面的几种方法 一 应用框架技术在页面中嵌入外部页面的 html 中怎么从一个页面跳转到另一个页面弄一个名字为网页跳转的

    2026年3月17日
    2
  • 页面刷新之reload()和refresh()的区别

    页面刷新之reload()和refresh()的区别window.reload()重新加载当前需要的所有内容,也就包括页面和后台的代码,此过程中实际上是从后台重新进行操作;window.refresh()是更新,保存以前的缓存文件內容,再次载入网

    2022年7月1日
    34

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号