【前向星】
不要把前向星想成什么高深莫测的东西,它其实就是一种边集数组。
前向星特别适合用来优化SPFA、DFS、BFS。
前向星构建过程如下:
先把图中每条边的起点按照从小到大的顺序排序,如果起点一样,那么把终点按照从小到大的顺序排序。此过程目的是确立图中边的位序。
构建数组head[i],记录图中以点i为起点的所有出边的第一个位序。
构建数组len[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[i]、len[i]值如下:
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))。
【链式前向星】
如果说邻接表是不好写但效率好,邻接矩阵是好写但效率低的话,前向星就是一个相对中庸的数据结构。前向星固然好写,但效率并不高。而在优化为链式前向星后,效率也得到了较大的提升。虽然说,世界上对链式前向星的使用并不是很广泛,但在不愿意写复杂的邻接表的情况下,链式前向星也是一个很优秀的数据结构。
——摘自《百度百科》
链式前向星其实就是静态建立的邻接表,时间效率为O(m),空间效率为O(m),遍历效率也为O(m)。
建立链式前向星,不需要对结点序号按大小进行排序。
以下面数据为例,第一行表示5个顶点7条边,接下来是7条边的起点、终点和权值。
5 7
1 2 1
2 3 2
3 4 3
1 3 4
4 1 5
1 5 6
4 5 7
链式前向星存储的是以1...n为起点的边的集合。在实现算法时,按边的输入顺序从0开始对边进行编号。这些编号,将帮助界定下文中陈述edge[i].next时的“上一条边”,及陈述head[i]时的“最后一条边”。
编码之前约定两个变量的含义:
edge[i].next,表示与边i起点相同的上一条边的编号。(由于惯例,此处next不改为pre,虽有可能造成误解)
head[i]数组,表示与点i起点相同的最后一条边的编号。(只是方便记忆才这样陈述。规范说法是以i为起始点。)
head数组一般初始化为-1。
加边函数如下:
void add_edge(int u, int v, int w){ //加边函数。u为起点,v为终点,w为边权
edge[cnt].to=v; //终点
edge[cnt].w=w; //权值
edge[cnt].next=head[u]; //以u为起点的的最后一条边的编号
head[u]=cnt++; //更新以u为起点的上一条边的编号
}
依据next,head数组的约定,并按边的输入顺序从0开始对边进行编号,然后按照上面的数据就可以写出下面的过程:
对于1 2 1这条边:edge[0].to=2; edge[0].next=-1; head[1]=0;
对于2 3 2这条边:edge[1].to=3; edge[1].next=-1; head[2]=1;
对于3 4 3这条边:edge[2].to=4; edge[2],next=-1; head[3]=2;
对于1 3 4这条边:edge[3].to=3; edge[3].next=0; head[1]=3;
对于4 1 5这条边:edge[4].to=1; edge[4].next=-1; head[4]=4;
对于1 5 6这条边:edge[5].to=5; edge[5].next=3; head[1]=5;
对于4 5 7这条边:edge[6].to=5; edge[6].next=4; head[4]=6;
遍历函数如下:
for(int i=1; i<=n; i++){ //n个起点
cout<
for(int j=head[i]; j!=-1; j=edge[j].next){ //遍历以i为起点的所有边
cout<
}
cout< } 第一层for循环:依次遍历以1...n为起点的边的集合。 【链式前向星代码】 5 7 1 2 1 2 3 2 3 4 3 1 3 4 4 1 5 1 5 6 4 5 7 1 //以1为起点的边的集合 1 5 6 1 3 4 1 2 1 2 //以2为起点的边的集合 2 3 2 3 //以3为起点的边的集合 3 4 3 4 //以4为起点的边的集合 4 5 7 4 1 5 5 //以5为起点的边不存在
第二层for循环:遍历以i为起点的所有边,j首先等于head[i]。注意:head[i]表示与点i起点相同的最后一条边的编号。
然后,通过edge[j].next来找与边j起点相同的上一条边的编号,终止条件为edge[j].next=-1。
完整代码为:#include
【测试样例】
in:
out:
【参考文献】
https://blog.csdn.net/sugarbliss/article/details/86495945
https://blog.csdn.net/acdreamers/article/details/16902023
https://blog.csdn.net/CSL201816080304/article/details/89198227
https://blog.csdn.net/LOOKQAQ/article/details/81304637
https://www.cnblogs.com/crazyacking/p/3761686.html