【图论】用链式前向星(邻接表)存有向图(图文代码逐句分析)

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为起点的所有点该怎么办呢?

  1. 找到h[1],1代表起点,h[1]=7,我们找到了边7
  2. 通过e[7]=6,我们知道了边7的终点为6,我们就这样找到了边1----->6,这条边编号为7
  3. 我们通过ne[7]=3,找到了同样起点为1的边3
  4. 通过e[3]=5,我们知道了边3的终点为5,我们就这样找到了边1----->6,这条边编号为3
  5. 重复3、4找到边1----->3,编号为1
  6. 查找到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,喜欢我的博客就点个赞吧,更多图论与数据结构知识点请见作者专栏《图论与数据结构》

  • 23
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值