邻接表的链式储存--链式前向星

博客介绍了链表存储邻接表的方法。使用邻接表存储图结构效率高,但用stl的vector表示邻接表可能因常数过大无法过题,可构建链表,链式前向星是不错选择。还详细解释了p[]、eid、e[]等变量在存储边信息时的含义和作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表存储邻接表的方法

当使用邻接表存储图结构时效率很高,但是当我们用stl的vector来表示邻接表的时候会因为vector过大的常数而不能过题,因此我们需要自己构建一个链表,链式前向星是一个不错的方法,但是不是很容易弄懂,下边是个人的理解。
首先明白每个变量的含义
p[] 表示每个顶点中边的数量(存的是基数所以实际上是边数减一),每次加边的时候都会修改p[]中的值;
p[u] u表示就是当前点,p[u]表示下标的索引,用e[p[u]]就可以访问到
eid表示当前是第几个边加入了,配合p[]一起使用;
e[]是最难理解的了,结构体edge将每个边的下一个顶点,每个边的权值,还有不好理解的next(待会解释)构建在一起,当我们每次插入边的时候,e[i]中的i表示当前是我们插入的第几个边了,v表示当前下一个边是谁,d表示距离,重要的是e[i].next = p[u];这段代码表示的意思是第i条边的下一个边是p[u](也就是在这个边之前还有几个边,我们需要去访问,而p[u]的值就刚好就表示了下一个索引),当我们没有改变u的时候e[i].next实际上存储的是上一条边的序号(可以理解成e[i-1]),当u改变的时候,又从-1从新开始记录,我们访问的时候只用从e[i].next开始一直访问下去就行了,只要他不是-1(-1表示u的第一个边,也就是访问完了)。

#include <iostream>
#include <cstring>
using namespace std;
const int M = 1000000;
const int N = 10000;
struct edge {
	int v, d, next;
} e[M];//M条边 
int p[N], eid; //N个点 
void init() {  // 初始化,在建图之前必须进行
	memset(p, -1, sizeof(p));
	eid = 0;
}
void insert(int u, int v, int d) {  // 插入单向边
	e[eid].v = v;
	e[eid].d = d;
	e[eid].next = p[u];
	p[u] = eid++;
}
void insert2(int u, int v, int d) {  // 插入双向边
	insert(u, v, d);
	insert(v, u, d);
}
int m;
void input() {
	cin >> m;//m条边 
	for (int i = 0; i < m; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		insert(u, v, w);
	}
}
void output(int m) {  // 输出整张图中的所有边
	for (int i = 0; i < m; i++) {
		for (int j = p[i]; j != -1; j = e[j].next) {  // 遍历从 i 连出的所有边
			cout << i << "->" << e[j].v << ", " << e[j].d << endl;
		}
	}
}
int main() {
	init(); 
	input();
	output(m);
	return 0;
}
<think>好的,用户想了解链式邻接表链式前向星的区别,包括数据结构和适用场景。我需要先回忆这两种结构的基本概念,然后比较它们的实现方式和应用场景。 首先,链式邻接表,我记得这是图的一种存储方式,每个顶点对应一个链表,存储其邻接点。比如引用[1]提到链式邻接表是基础的高级存储结构之一。而链式前向星,引用[4]说它本质是链表,但多了head数组记录头结点,可能更高效? 接下来看具体实现。链式邻接表每个顶点维护一个链表,每个节点包含邻接顶点和权重等信息,可能还有下个节点的指针。而链式前向星,根据引用[4]的代码,结构体EDGE包含v、w、next,通过head数组和边的添加方式来连接。看起来前向星将所有边存在一个数组中,通过索引来链接,而邻接表每个顶点独立链表。 空间方面,邻接表每个边都需要存储指针,而前向星可能更紧凑,用数组代替指针,减少内存碎片。引用[4]提到前向星的空间效率是O(m),适合稠密图?或者是不是适合稀疏图?可能记错了,需要再确认。 适用场景的话,邻接表实现直观,适合动态增删边,但指针可能占用额外空间。前向星用数组模拟链表,访问连续内存,缓存友好,适合处理大规模数据,尤其是静态图。引用[3]和[4]提到邻接矩阵、邻接表、前向星三种方式,前向星在空间和时间上可能更优。 可能用户混淆了两种结构,需要明确它们的存储方式、实现细节和性能差异。比如,邻接表的每个顶点链表是独立的,而前向星通过全局数组和head指针来管理,遍历时可能更高效。另外,前向星的边添加是头插法,所以遍历顺序是逆序的,但这对某些算法不影响。 总结区别点:数据结构实现、存储方式、内存使用、适用场景(动态vs静态,稀疏vs稠密)。需要指出各自的优缺点,帮助用户理解何时选择哪种结构。</think>### 链式邻接表 vs 链式前向星:数据结构区别及适用场景 #### 一、数据结构区别 1. **链式邻接表** - **核心结构**:每个顶点维护一个独立的链表链表中节点存储邻接顶点的信息(如顶点编号、边权值等)[^1][^3]。 - **实现方式**:通过动态内存分配或结构体指针实现链表,例如: ```c struct Node { int v; // 邻接顶点 int w; // 边权值 Node* next; // 指向下一个邻接点 }; Node* head[N]; // 每个顶点链表头指针 ``` 2. **链式前向星** - **核心结构**:通过数组模拟链表,使用静态存储结构。所有边存储在一个数组中,每条边记录邻接顶点、权值以及“下一条边的索引”[^4]。 - **实现方式**: ```c struct EDGE { int v, w, next; // next指向同起点的上一条边的索引 } e[M]; // 边数组 int head[N]; // head[u]记录顶点u的最后添加边的索引 int tot = 1; // 当前边的总数 ``` #### 二、关键差异对比 | **特性** | **链式邻接表** | **链式前向星** | |----------------|----------------------------------|----------------------------------| | **存储方式** | 动态链表(指针) | 静态数组模拟链表 | | **内存占用** | 较高(需存储指针) | 较低(仅需数组索引)[^4] | | **访问效率** | 可能因内存碎片降低缓存命中率 | 数组连续存储,缓存友好 | | **动态增删边** | 支持(直接操作链表) | 不支持(需预先分配数组) | | **遍历顺序** | 正序(按添加顺序) | 逆序(头插法导致)[^4] | #### 三、适用场景 1. **链式邻接表** - **推荐场景**:需要频繁动态增删边的图(如社交网络动态更新)[^3]。 - **缺点**:内存碎片化问题,对大规模稠密图不友好。 2. **链式前向星** - **推荐场景**:静态图或边数已知的稠密图(如竞赛题目、固定路网)[^4]。 - **优点**:内存紧凑、访问高效,适合需要高性能遍历的场景。 #### 四、代码示例 1. **链式邻接表添加边** ```c void add_edge(int u, int v, int w) { Node* new_node = (Node*)malloc(sizeof(Node)); new_node->v = v; new_node->w = w; new_node->next = head[u]; head[u] = new_node; } ``` 2. **链式前向星添加边** ```c void add(int u, int v, int w) { e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; // 头插法 head[u] = tot++; } ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值