图的存储:链式向前星

内容摘要
1. 概念链式向前星代码是基于向前星代码的优化,这是极大多数算法竞赛以及高效率图论算法喜欢适用的创建方法,与邻接表和邻接矩阵比较容易的理解方式,向前星算法并不容易理解。
文章正文

1. 概念

链式向前星代码是基于向前星代码的优化,这是极大多数算法竞赛以及高效率图论算法喜欢适用的创建方法,与邻接表和邻接矩阵比较容易的理解方式,向前星算法并不容易理解。

在理解链式向前星之前我们需要了解什么是向前星,前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了。

链式向前星的本质是利用链表的特性(一个结点指向另一个结点),以达到不需要像向前星那样排序(排序的平均情况需要O(nlogn)的代价)就可以直接搜索到目标,从而达到减少计算机运算时间使用的情况。

 

2.结构设计

与前文不同,本文我们先展示代码再做具体讲解,链式向前星的结构模板代码如下:

struct Edge{    //表示边
    int w;
int to;
    int next;
}edge[10005];
 
int cnt=0;      //用以控制并统计边的数量
 
int head[10005];    //表示来源的边序号

具体的解释为:

a)Edge表示边,这个结构体数组将逐步记录边信息,其中包含有三个元素

l  w为权重即边之间的权值,下图中为默认的1,不演示

l  to表示为第i条边指向哪一个结点

l  edge[i].next表示第i条边的下一条边的序号

b)Cnt表示边的数量,在输入时利用他逐个+1

c)Head表示第x个结点所需要访问的边

同样的我们以这个结构的图为例,链式向前星中需要存储如下内容:

数据结构98

(例图,并且假设所有边的权值均为1)

上图可以得到一个这样的运算表格(不唯一)

 

Edge[0].to 2 Edge[0].next -1 Head[1] 0
Edge[1].to 3 Edge[1].next 0 Head[1] 1
Edge[2].to 4 Edge[2].next -1 Head[2] 2
Edge[3].to 5 Edge[3].next 2 Head[2] 3
Edge[4].to 4 Edge[4].next -1 Head[3] 4
Edge[5].to 5 Edge[5].next -1 Head[4] 5

可以见的,比如我们访问与1相互联通的所有结点,我们首先访问head[1]的内容,head的下标表示1结点,其内容表示我们应该访问边的标号,此时我们得到了数据1,表明我们需要访问边1,此时我们找到edge[1]并获取第一个to的内容,表示1结点与3结点相连通,接下来访问next的内容,在edge[1].next中获得了下一条边的标号0,因此接下来访问edge[0]的内容,得到了新得信息,edge[0].to=2,表示1结点与2结点相互联通,在访问next的内容为-1时表示没有下一条了,结束向下访问,自此,我们获得了与1相互联通的所有结点的信息。

因此可以得到如下的信息表:

结点1 -1 2 3
结点2 1 4 5
结点3 -1 4  
结点4 -1 5  
结点5 -1    

添加边信息时使用以下代码

void add_edge(int from, int to, int w) {
    edge[cnt].to = to;
    edge[cnt].w = w;
    edge[cnt].next = head[from];
    head[from] = cnt++;
}

 

 

注意,我们需要对全体数组进行赋-1的初值,这对于出错和检验错误都是很有帮助的,因为-1正是本算法的判定边界点,当然,这个边界点也可以由自己定位任意一个负数。

3. 优势

链式向前星的有点在于高效,访问速度较快,是图论算法的热门构建方法,这点在算法竞赛中体现尤为重要,缺点也很明显,不易理解和构造麻烦。

链式本身就有访问此结点会自动体现下一节点的效果,因此很适合遍历和访问的算法代码构建,这点在后文会提到。

 

注:建议初学者多次阅读本章内容

代码注释
[!--zhushi--]

作者:喵哥笔记

IDC笔记

学的不仅是技术,更是梦想!