c++图论vector邻接表与链式前向星性能差异

一句话概括:如果以后能用链式前向星的话(知道边数的情况下)就尽量用链式前向星别用vector邻接表

因为我以前学图论的时候,先接触到的就是vector邻接表的写法,所以后来一直都是用vector邻接表的写法,后来也接触到了链式前向星的写法,然后那时候也了解到了vector邻接表与链式前向星有内存性能上的差异,因为vector扩充时是默认多申请2倍空间,所以一些特别变态的题目可能会卡内存只能用链式前向星的写法写。不过当时也没特别在意,也想着到时候看边数量特别大的话再说,vector邻接表也写惯了,可是最近居然被卡了两道时间的题目,让我意识到可能链式前向星写法与vector写法还有时间上的差距,我猜与STL实现上有关。(更新:最近看STL源码剖析,vector每次扩充都要copy一遍元素到新内存块,肯定会慢很多的)

我就拿其中一题来举例吧

 

问题描述

农夫约翰正在针对一个新区域的牛奶配送合同进行研究。他打算分发牛奶到T个城镇(标号为1..T),这些城镇通过R条标号为(1..R)的道路和P条标号为(1..P)的航路相连。

每一条公路i或者航路i表示成连接城镇Ai(1<=A_i<=T)和Bi(1<=Bi<=T)代价为Ci。每一条公路,Ci的范围为0<=Ci<=10,000;由于奇怪的运营策略,每一条航路的Ci可能为负的,也就是-10,000<=Ci<=10,000。

每一条公路都是双向的,正向和反向的花费是一样的,都是非负的。

每一条航路都根据输入的Ai和Bi进行从Ai->Bi的单向通行。实际上,如果现在有一条航路是从Ai到Bi的话,那么意味着肯定没有通行方案从Bi回到Ai。

农夫约翰想把他那优良的牛奶从配送中心送到各个城镇,当然希望代价越小越好,你可以帮助他嘛?配送中心位于城镇S中(1<=S<=T)。

输入格式

输入的第一行包含四个用空格隔开的整数T

<think>好的,我现在需要帮助用户找到关于链式前向星数据结构模板,可能用于图算法实现。首先,我得回忆链式前向星的基本概念结构。根据用户提供的引用内容,链式前向星是图的一种存储方式,常用于算法竞赛高效率图论算法,是对向前星代码的优化。用户引用的资料提到它比邻接表邻接矩阵更高效但更难理解。 接下来,我需要整理链式前向星的组成部分。通常,它包含三个数组:边数组(存储边的信息)、头数组(记录每个顶点的第一条边)next数组(指向下一条边的索引)。根据引用[3]中的例子,比如ne数组的结构,可以推断出每个节点通过头数组next数组链接起来,形成链表结构。 然后,考虑如何构建链式前向星。添加边的时候,使用头插法,更新头数组next数组。这部分需要详细解释,可能还要给出代码示例。用户提供的引用中有C++的代码片段,但用户可能需要不同语言的模板,不过根据引用内容,C++是主要提到的语言,所以应该以C++为例。 另外,遍历链式前向星的过程也很重要。需要从头数组出发,沿着next数组依次访问每个节点的邻边。这部分需要代码示例说明如何遍历,比如在Dijkstra算法中的应用,如引用[2]提到的。 用户可能还需要了解链式前向星的优缺点,比如空间效率高,适合稀疏图,但理解实现起来比较复杂。需要对比邻接表邻接矩阵,解释为什么链式前向星更优。 最后,生成相关问题时要围绕链式前向星的应用、优化、对比其他结构以及具体算法中的使用,比如最短路径问题中的Dijkstra算法,如引用[2]中的例子。 需要确保回答的结构清晰,分点说明实现步骤,给出代码模板,并解释每个部分的作用。同时,引用提供的资料来支持内容,比如引用链式前向星的构建遍历方法,以及具体的代码结构。</think>### 链式前向星数据结构实现指南 链式前向星是一种高效存储稀疏图的数据结构,通过**头插法链表**管理边的连接关系。其核心组件包含: - `head[]`:顶点索引表,存储每个顶点的最新边编号 - `edge[]`:边数据容器,记录目标顶点边权 - `next[]`:链表指针,维护邻接边的遍历顺序 - `idx`:动态边计数器 #### C++模板实现 ```cpp #include <vector> using namespace std; const int MAXN = 1e5 + 5; // 最大顶点数 const int MAXM = 2e5 + 5; // 最大边数 struct Edge { int to; // 目标顶点 int w; // 边权(可选) int next; // 下条边索引 } edge[MAXM]; int head[MAXN]; // 顶点索引表 int idx = 0; // 边计数器 // 初始化图结构 void init() { fill(head, head + MAXN, -1); idx = 0; } // 添加有向边 void add_edge(int u, int v, int weight) { edge[idx].to = v; edge[idx].w = weight; edge[idx].next = head[u]; // 头插法建立链表 head[u] = idx++; // 更新顶点最新边 } // 添加无向边(双向添加) void add_undirected(int u, int v, int w) { add_edge(u, v, w); add_edge(v, u, w); } ``` #### 遍历邻接边示例 ```cpp // 遍历顶点u的所有邻边 void traverse(int u) { for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; int w = edge[i].w; // 处理边(u->v)的权值w } } ``` ### 结构对比分析 | 存储方式 | 空间复杂度 | 查询效率 | 适用场景 | |----------------|------------|----------|----------------| | 邻接矩阵 | $O(n^2)$ | $O(1)$ | 稠密图 | | 邻接表 | $O(n+m)$ | $O(d)$ | 通用场景 | | **链式前向星** | $O(m)$ | $O(d)$ | 稀疏图/竞赛场景[^1] | ### 算法应用场景 1. **最短路径算法**:Dijkstra算法中高效访问邻接边[^2] 2. **网络流算法**:Ford-Fulkerson方法中的残余网络表示 3. **图遍历算法**:DFS/BFS的邻接边迭代
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值