前向星和链式前向星(详解+模板)
前向星和链式前向星
参考博客:深度理解链式前向星
什么是前向星
前向星是一种特殊的边集数组,我们把边集数组中的每一条边按照起点从小到大排序,如果起点相同就按照终点从小到大排序,并记录下以某个点为起点的所有边在数组中的起始位置和存储长度,那么前向星就构造好了。
用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(1)的时间内找到以i为起点的第一条边以O(len[i])的时间找到以i为起点的所有边。前向星特别适合用来优化SPFA、DFS以及BFS。
但是,在这里有一个问题,就是前向星还是需要加上排序的时间,如果是快排大概是O(n*log(n));而链式前向星则不需要排序也能得到。
链式前向星
我觉得,直接不好理解链式前向星,我们先来看其代码,然后通过其代码来体味其中的道理:
struct NODE{int w;int to;int next; //next[i]表示与第i条边同起点的上一条边的储存位置
}edge[MAXN];
int cnt = 0;
int head[MAXN]; //head初始化为-1
void add(int u,int v,int w){edge[cnt].w=w;edge[cnt].to=v; //edge[i]表示第i条边的终点 edge[cnt].next=head[u]; //head[i]表示以i为起点的最后一条边的储存位置 head[u]=cnt++;
}
其中:
edge[i].to表示第i条边的终点
edge[i].next表示与第i条边同起点的下一条边的存储位置
edge[i].w为边的权值。
**数组head[]**它是用来表示以i为起点的第一条边存储的位置
注意head[]数组一般初始化为-1
给出代码我们可能还是很难理解,我们用前面那个例子模拟一遍就会有所理解了:
样例:
其中边的输入顺序:
1 2
2 3
3 4
1 3
4 1
1 5
4 5
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的第一条起始边的位置。而head[i]中的值,就是代表以i为起点的所有边中编号最大的那条边在edge[]数组中的下标。
我们再来看看怎么遍历:
for(int i=head[u];~i;i=edge[i].next)
比如这里u=1,指的是我们遍历所有以1为起点的边,首先是编号为5的边(edge[5]),其中点是5(edge[5].to = 5),下一个是编号为3的边(edge[5].next = 3)…直到edge[].next = -1。至此就遍历完了所有以1为起点的边。
这会,我们再来回过头看链式前向星的代码就很好理解了:
首先,对于add函数中前两行代码,其目的就是为了保存该条边的权值及其终点。关键在于下面两行代码:在更新head[u]之前我们需要先保存当前的head[u],然后再更新head的编号(cnt)。
SPFA+链式前向星建图
/******链式前向星建图********/
struct NODE{int w;int to;int next; //next[i]表示与第i条边同起点的上一条边的储存位置
}edge[MAXN];
int cnt = 0;
int head[MAXN]; //head初始化为-1
void add(int u,int v,int w){edge[cnt].w=w;edge[cnt].to=v; //edge[i]表示第i条边的终点 edge[cnt].next=head[u]; //head[i]表示以i为起点的最后一条边的储存位置 head[u]=cnt++;
}
/***************/int dis[MAXN];//dis[i]=源点s->i最短路径
bool vis[MAXN];//vis[i]表示i是否在队列
void spfa(int s)
{for(int i=1;i<=MAXN;i++)//初始化{dis[i]=INF;vis[i]=true;}dis[s]=0;//源点到自身距离为0queue<int>q;//使用c++自带队列q.push(s);//源点入队vis[s]=false;while(!q.empty())//若队列不为空{int u=q.front();//取出队首元素弹出q.pop();vis[u]=true;for(int i=head[u];~i;i=ed[i].next){int v=ed[i].to;if(dis[u]+ed[i].w<dis[v]){dis[v]=dis[u]+ed[i].w;if(vis[v])//如果终点不在队列{q.push(v);vis[v]=false;}}}}
}
前向星和链式前向星(详解+模板)相关推荐
- spfa(链式前向星)+dijkstra(链式前向星)
链式前向星 链式前向星可以存图, 它存图的方式是: 将任意一个节点的所有临边按输入顺序依次连接起来将任意一个节点的所有临边按输入顺序依次连接起来将任意一个节点的所有临边按输入顺序依次连接起来 然后头节 ...
- 链式前向星dij堆优化
链式前向星 nextnextnext指的是上一条同起点边的位置,tototo表示这条边的终点,valvalval表示边权. u.v.valu.v.valu.v.val分别表示起点,终点,边权. hea ...
- 链式前向星——最完美图解
图的存储方法很多,最常见的除了邻接矩阵.邻接表和边集数组外,还有链式前向星.链式前向星是一种静态链表存储,用边集数组和邻接表相结合,可以快速访问一个顶点的所有邻接点,在算法竞赛中广泛应用. 链式前向星 ...
- 链式前向星(超详细)
前向星和链式前向星 链式前向星 添加边的操作 链式前向星,就是以链式结构来储存前向星,每条边就要用结构体,结构体中包含三个数据: Edge[i].to :第i条边的终点 Edge[i].next :与 ...
- 链式前向星加边操作图解
[知识点解析] 图的存储方法很多,除了最常见的邻接矩阵.邻接表外,还有链式前向星. 链式前向星是用数组模拟邻接表存图的一种优秀的数据结构,是一种特殊的边集数组.它在算法竞赛中被广泛应用. 从链式前向星 ...
- Balkan2007]Toponyms[链式前向星建字典树+getchar()读入优化]
思路容易想,卡空间和时间就吐了 用链式前向星压缩空间,用getchar()一位一位读加快读入 #include <iostream> #include <cstdio> #in ...
- P3366 【模板】最小生成树(链式前向星,prim,有坑)难度⭐⭐
题目链接 输入: 4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3 输出: 7 链式前向星相比于矩阵,遍历的代码更加复杂一点,但是省空间,这道题用矩阵存就MLE,只能用链式前向星存. ...
- P1155 双栈排序(二分图的染色判断+链式前向星)
P1155 双栈排序 让字典序最小,当然尽量进S1 那什么时候必须进S2呢? a[i]和a[j] 不能压入同一个栈⇔存在一个k,使得i<j<k且a[k]<a[i]<a[j] 因 ...
- 邻接表存储(链式前向星)
细节 链式前向星: head[i] 存储顶点i当前出边的编号 to[num] 存储当前出边的终点 w[num] 存储当前出边的权值 next[num] 存储上一条从顶点i出发的边的边号 N个顶点对应N ...
最新文章
- MySQL Sending data导致查询很慢的问题详细分析
- 操作系统中的P-V操作(转)
- flink 不设置水印_区分理解Flink水印延迟与窗口允许延迟的概念
- 适用于特殊类型自然语言分类的自适应特征谱神经网络
- Dynamics CRM2013 6.1.1.1143版本号插件注冊器的一个bug
- Linux shell脚本中判断变量文件目录:权限、是否存在、空值、相等
- 在项目中使用NeatUpload
- 博客在微博中怎么添加html,新浪微博怎么进博客
- DBF文件实例分析(转)
- 使用calibre给电子书生成目录
- mysql 5.7安装vsvcr_MySql 5.7 中文文档 - 2.1.3.2 使用 GnuPG 进行签名检查 | Docs4dev
- iOS实现绘画文字动画
- Java利器之UML类图详解
- [二] X 名称空间
- 完成迭代效劳器端和客户端
- arduino学习笔记五
- Java的基础重要吗?Java入门应该学习那些?
- 动态规划 | 可以用在核酸检测的算法:莱文斯坦算法
- matlab三电平statcom无功检测双闭环svpwm调制两电平/三电平逆变器拓扑
- bat批处理之启动多个PC端微信