【链式前向星+存图】讲解

大佬的链式前向星:https://blog.youkuaiyun.com/acdreamers/article/details/16902023

【前向星】:

解释一下:

 【前向星和链式前向星的不同】:

 【给出链式前向星的代码实现】:

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100501
struct NODE{
	int w;
	int e;
	int next; //next[i]表示与第i条边同起点的上一条边的储存位置
}edge[MAXN];
int cnt;
int head[MAXN]; 
void add(int u,int v,int w){
	edge[cnt].w=w;
	edge[cnt].e=v;    //edge[i]表示第i条边的终点 
	edge[cnt].next=head[u]; //head[i]表示以i为起点的最后一条边的储存位置 
	head[u]=cnt++;
}
int main(){
	memset(head,0,sizeof(head));
	cnt=1;
	int n;
	cin>>n;
	int a,b,c;
	while(n--){
		cin>>a>>b>>c;
		add(a,b,c);
	}
	int start;
	cin>>start;
	for(int i=head[start];i!=0;i=edge[i].next)
	   cout<<start<<"->"<<edge[i].e<<" "<<edge[i].w<<endl;
	return 0;
}

 结果:

【分析一下】:

注意cnt的初值我们初始化为1                  cnt
edge[1].next = head[1] = 0, head[1] = 1,  2 ;
edge[2].next = head[2] = 0, head[2] = 2,  3 ; 
edge[3].next = head[3] = 0, head[3] = 3,  4 ; 
edge[4].next = head[1] = 1, head[1] = 4,  5 ; 
edge[5].next = head[4] = 0, head[4] = 5,  6 ; 
edge[6].next = head[1] = 4, head[1] = 6,  7 ;

模拟:当start=1时,循环变为 for(i = head[1];i!= 0;i = edge[1].next) 通过输出语句将第六条边的终点和权值(就是边的值)输出出来,同时i = edge[6].next = 4(相当于一个链表将第四条边牵出来),然后将第四条边的终点和权值输出,这是i = edge[4].next = head[1] = 1,又将第一条边牵出来,输出第一条边的终点和权值,然后i=0,循环结束,其他类似。 

 【边是倒序遍历的】:

 

贴个链接:点击这里!(好理解的一篇博客)

<think>嗯,用户之前问了Prim算法,现在又问链式前向星。可能他们正在学习图论相关的算法,特别是储方式。链式前向星是一种常用的的表示方法,特别是在处理需要高效遍历边的场景,比如最短路径或网络流问题。用户可能已经了解邻接矩阵和邻接表,但想进一步学习更高效的储结构,尤其是在空间优化方面。 用户的需求是希望用C++实现链式前向星,所以需要详细解释其结构、优缺点以及具体的代码实现。他们可能对指针和动态内不太熟悉,或者希望避免使用STL的vector,所以链式前向星用数组模拟链表会更适合。需要确保代码示例清晰,并解释每个部分的作用,比如边的结构、头数组、边的添加方式等。 另外,用户可能对链式前向星的优势和应用场景不太清楚,需要对比邻接表,说明为什么在某些情况下链式前向星更优,比如空间效率和缓友好性。同时,注意提到它的缺点,比如不易于删除操作,这样用户能全面理解何时使用这种结构。 需要检查自己的知识是否正确,链式前向星确实是使用数组来模拟链表,通过头数组记录每个顶点的第一条边,然后通过next指针遍历所有边。代码中的add_edge函数需要正确维护这些指针,确保边的插入顺序正确,避免混淆。 可能用户在实际编码中遇到了性能问题,所以想用更高效的数据结构。需要确保示例代码正确,比如边的索引从0开始还是1开始,是否会导致越界等问题。同时,遍历的时候要逆序,因为每次插入到链表头部,这点需要特别指出,避免用户误解遍历顺序。 最后,总结部分要强调链式前向星的适用场景,比如需要频繁遍历邻接边且空间受限的情况,帮助用户决定何时使用这种结构。确保解释清晰,代码注释详细,方便用户理解和应用。</think>链式前向星是一种高效的数据结构,特别适合处理大规模稀疏。它通过数组模拟链表的方式储邻接关系,兼具空间效率和遍历速度。以下是详细解析: --- ### **一、核心结构解析** 采用 **数组模拟链表** 的设计,包含三个关键数组: 1. `edge数组`:储所有边的信息 2. `head数组`:记录每个顶点最新的边索引 3. `next数组`:储同顶点的下一条边索引 ![链式前向星结构示意](https://pic1.zhimg.com/80/v2-5c4c4f8a3a0e3b1e4d7d6b3a3d3b3b3d_720w.png) --- ### **二、储原理** 1. **边储方式**: ```cpp struct Edge { int to; // 边指向的顶点 int weight; // 边权值 // 可扩展其他属性 }; Edge edges[MAX_EDGES]; // 边储数组 int head[MAX_NODES]; // 头指针数组 int next[MAX_EDGES]; // 下一条边索引 int cnt = 0; // 当前边计数 ``` 2. **添加边操作**(以有向为例): ```cpp void add_edge(int from, int to, int weight) { edges[cnt].to = to; edges[cnt].weight = weight; next[cnt] = head[from]; // 新边指向旧头 head[from] = cnt++; // 更新头指针 } ``` --- ### **三、遍历邻接边示例** 遍历顶点`u`的所有出边: ```cpp for(int i = head[u]; i != -1; i = next[i]) { Edge e = edges[i]; // e.to 是邻接顶点 // e.weight 是边权值 } ``` --- ### **四、完整C++实现模板** ```cpp #include <iostream> #include <cstring> // 用于memset const int MAX_NODES = 100010; const int MAX_EDGES = 200020; // 无向需双倍空间 struct Edge { int to; int weight; // int next; // 不需要,单独维护next数组 }; Edge edges[MAX_EDGES]; int head[MAX_NODES]; int next_edge[MAX_EDGES]; // 单独的下一条边索引 int edge_cnt = 0; void init() { memset(head, -1, sizeof(head)); // 初始化为-1 } // 添加有向边 void add_directed_edge(int from, int to, int weight) { edges[edge_cnt].to = to; edges[edge_cnt].weight = weight; next_edge[edge_cnt] = head[from]; head[from] = edge_cnt++; } // 添加无向边(需调用两次) void add_undirected_edge(int u, int v, int weight) { add_directed_edge(u, v, weight); add_directed_edge(v, u, weight); } // 遍历示例 void traverse_edges(int u) { std::cout << "顶点" << u << "的邻接边:\n"; for(int i = head[u]; i != -1; i = next_edge[i]) { Edge e = edges[i]; std::cout << "-> " << e.to << " (权重: " << e.weight << ")\n"; } } int main() { init(); // 添加示例边 add_undirected_edge(0, 1, 5); add_undirected_edge(0, 2, 3); add_undirected_edge(1, 3, 2); // 遍历顶点0的邻接边 traverse_edges(0); return 0; } ``` --- ### **五、核心优势分析** 1. **空间效率**:O(E)储空间,远优于邻接矩阵的O(V²) 2. **缓友好**:数组储比链表节点更紧凑 3. **动态扩展**:不需要预分配固定大小 4. **遍历效率**:O(1)头访问 + O(k)邻接边遍历(k为邻接边数) --- ### **六、与邻接表对比** | 特性 | 链式前向星 | 邻接表(vector) | |--------------------|---------------------|----------------------| | 内分配 | 静态数组预分配 | 动态内分配 | | 空间利用率 | 更高(无指针开销) | 较低(指针或结构体) | | 遍历速度 | 更快(连续内) | 略慢(可能内跳跃) | | 删除操作 | 困难 | 较容易 | | 代码复杂度 | 较高 | 较低 | --- ### **七、应用场景推荐** 1. 算法竞赛中的大规模稀疏 2. 需要频繁遍历邻接边的场景(如Dijkstra、SPFA) 3. 内受限环境下的储 4. 需要快速初始化清空的场景(只需重置head数组) --- ### **八、注意事项** 1. 边的遍历顺序是 **逆序插入顺序**(类似头插法链表) 2. 无向添加边需要正反各加一次 3. 数组大小需根据题目要求设置(通常MAX_EDGES设为最大边数的2倍) 4. 初始化时head数组必须置为-1 通过这种结构,我们可以在不损失效率的前提下,用最紧凑的方式结构,特别适合处理算法竞赛中的大规模图论问题。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值