c++链式前向星代码分析(代码逐句分析)
链式前向星(优化后的邻接表)是一种比较好用的存有向图的方法,相比邻接矩阵,链式前向星有着更大的优势。但是相比于前两种存图方式,链式前向星的代码难度是比较大的,笔者在学习时也花了不少时间。而看了很多博客虽然写的很好,但如果面对真正的纯小白(比如我),还是有点难以理解的。
链式前向星概念解释
在链式前向星中,我们需要4个数组与一个常量:h[N], ne[N], e[N](或者写为to[N]), w[N],cnt;
N代表边的编号,由cnt确定
其中,ne[N], e[N](或者写为to[N]), w[N],可以用结构体封装,熟练了也可以不封装,看个人喜好。ne[i], e[i](或者写为to[i]), w[i],共同确定一条编号为i的边
1.e[i](或者写为to[i]):编号为i的边的终点为e[i]。
2. w[i]:编号为i的边的长度为w[i]。
3. h[x]:(重点) 以x为起点的编号最大的边的编号为h[x],这里的x不要和上面的i混淆,也可以理解为最后存进图的以x为起点的边的编号为h[x]。初始化为-1,随着点的录入而更新。
4. ne[i](或写为next[i])(重点):当第i点录入边a---->b时,用ne[i]来记录下来当前的h[a],ne[i]代表在边i之前一个被存进图中的以a为起点的编号。用处在之后会解释到
5. cnt:用来记录上面的i,每录入一条边,cnt++
链式前向星功能实现解释
在了解了链式前向星的基本概念后,我们要怎么才能实现图的遍历呢?
首先,我们给出有向图的一部分,红字代表边的编号(即i),假设所有边的长度为1,即所有w[i]=1,录入图的顺序为1,3,7;
此时:
h[1]=7;
ne[1]=-1; ne[3]=1 ; ne[7]=3 ;
e[1]=3; e[3]=5; e[7]=6;
至于为什么是这样,可以看看上下文解释
然后,我们要找出以1为起点的所有点该怎么办呢?
- 找到h[1],1代表起点,h[1]=7,我们找到了边7
- 通过e[7]=6,我们知道了边7的终点为6,我们就这样找到了边1----->6,这条边编号为7
- 我们通过ne[7]=3,找到了同样起点为1的边3
- 通过e[3]=5,我们知道了边3的终点为5,我们就这样找到了边1----->6,这条边编号为3
- 重复3、4找到边1----->3,编号为1
- 查找到ne[1]=-1,我们知道了边1是最早被录入的以1为起点的边了,代表我们已经找完了所有以1为起点的边了,结束搜索。
通过这样的方法,我们只需要遍历所有图中所有的点x,寻找h[x]后按照上述步骤来遍历就可以遍历一个有向图中的所有的边了
链式前向星代码实现
核心代码实现
//初始化
const int N=1e5+7;
int e[N], ne[N], h[N],w[N] cnt = 1; //链式前向星
for (int i = 1; i <= n; i++) //把所有head初始化为-1,代表以i为起点的序号最小的边的next值为-1
h[i] = -1;
//存边
void add(int x,int y,int z) // 存x----->y,长度为z的边
{
e[cnt]=y;
w[cnt]=z;
ne[cnt]=h[x];
h[x]=cnt;
cnt++;
}
//找边
for(int i=1;i<=n;i++) //枚举图中所有点
{
for(int j=h[i];j!=-1;j=ne[j])
{
int k=e[j]; //这样就枚举完了所有边j:i------>k
······
}
}
全部代码实现,这里也写一下结构体写法
#include<iostream>
using namespace std;
struct Edge //每一个edge代表一条边,数组序号代表编号
{
int next; //上一个同起点的边的序号
int to; //这条边指向的点
int w; //这条边的长度
};
Edge edge[101]; //先定义个101条边
int head[101]; //head[i],代表以i为起点的边的最大序号是head[i],随着数组的输入而更新
int cnt = 1; //工具变量,记录边的序号,顺便(真的是顺便,这个功能非必要)记录了有几条边,add函数用一次自加一次
void add(int u, int v, int w) //输入从u到v的长度为w的边
{
edge[cnt].w = w; //第cnt条边长为w
edge[cnt].to = v; //第cnt条边指向点v
edge[cnt].next = head[u]; //上一条以u为起点的最大序号的边的序号是head[u],每一条边都记录下了上一个同起点的边的序号,用next
head[u] = cnt++; //现在以u为起点的最大序号的边的序号是cnt了
}
int main()
{
for (int i = 0; i < 101; i++) //把所有head初始化为-1,代表以i为起点的序号最小的边的next值为-1
head[i] = -1;
int m, n; //m个点,n条边
cin >> m >> n;
for (int i = 1; i <= n; i++) //边的输入
{
int u, v,w;
cin >> u >> v>>w;
add(u, v, w);
}
for (int i = 1; i <= m; i++) //输出每个以i为起点的所有线
{
for (int j = head[i]; j != -1; j=edge[j].next) //从以i为起点的最大序号边开始,通过每条边的next递推从而依次按边的序号从大到小输出边,到j=-1就说明输出完了
{
cout <<"这一条边是 "<< i << " --->" << edge[j].to << " 长度为" << edge[j].w << endl;
}
}
return 0;
}
例如,如果输入
5 7
1 5 3
2 5 4
3 5 1
3 2 4
3 6 6
4 2 1
5 4 2
程序将会输出
这一条边是 1 --->5 长度为3
这一条边是 2 --->5 长度为4
这一条边是 3 --->6 长度为6
这一条边是 3 --->2 长度为4
这一条边是 3 --->5 长度为1
这一条边是 4 --->2 长度为1
这一条边是 5 --->4 长度为2
这样链式前向星的问题就这样解决啦,是不是很简单(不是 )
作者:Avalon Demerzel,喜欢我的博客就点个赞吧,更多图论与数据结构知识点请见作者专栏《图论与数据结构》