链式前向星

前言

邻接表的真实形态!
听说网络流不能用 vector 了,于是来学。

1. 原理简述

区别于使用 vector 存图,链式前向星才是邻接表的真实形态(而非 vector)。链式即使用链表数据结构;前向即在头节点处插入新边;星即指针。用链表存图可以有效避免 vector 常数过大的问题,虽然开了 O2 都一样

2. 实现

2.1 初始化

链式前向星需要一个数组 _head[N] 表示每一个节点的邻接节点,初始化全部为 -1。之后需要一个结构体数组 edge[M] 和边数计数器 e_cntedge[M] 中存放的是每个边的信息,结构体中有三个参数:指向的节点编号 v,边权 w,下一条邻接边的下标 nxt。表现为以下代码:

struct e{
	int v;// 指向的节点编号
	int w;// 边权
	int nxt;// 下一条邻接边的下标
}edge[M];
int e_cnt=-1;
int _head[N];//意思大致相当于 vector<e> edge[N]

2.2 插入边

为了方便,我们实现一个函数 add(int u,int v,int w) 来插入边。
_head[u] 为初值,即节点还没有邻接点的情况时,直接在 _head[u] 后连接这条边,代码表现如下:

if(_head[u]==-1){
	++e_cnt;
	_head[u]=e_cnt;//边在存边数组 e[M] 中的下标为 e_cnt
		
	//w,v,nxt 描述这条边 
	edge[e_cnt].v=v;
	edge[e_cnt].w=w;
	edge[e_cnt].nxt=-1;//其后没有邻接边,赋值为 0 
}

_head[u] 已有邻接点时,我们要把新边插入到链表的最前端。很明显,链表不支持随机访问,但支持以 \(O(1)\) 复杂度在任意处插入新节点。所以如果往后插入必然会造成更多的时间开销,因此在头部插入最省时间。插入的方法即链表插入节点的过程,这里不多赘述,表现为如下代码:

else{
	++e_cnt;
	//先描述这条边 
	edge[e_cnt].v=v;
	edge[e_cnt].w=w;
	
	edge[e_cnt].nxt=_head[u];//新节点的下一个是此时头节点的下一个 
	_head[u]=e_cnt;//把头节点的下一个更新为新节点 
}

事实上我们发现,两个不同的情况可以写在一起:

void add(int u,int v,int w){
	e_cnt++;
	edge[e_cnt].v=v;
	edge[e_cnt].w=w;
	edge[e_cnt].nxt=_head[u];
	_head[u]=e_cnt;
} 

2.3 遍历

对于一个节点编号为 u,遍历其所连接的每一个节点,其实就是遍历从 _head[u] 开始的一个链表,用 while 循环即可实现,代码如下:

int cur=_head[u];// 从 _head[u] 开始 
while(cur!=-1){//这个节点没有邻接节点不进行遍历
	/* 
		do something
	*/
	if(e[cur].nxt==-1)break;//当当前节点没有下一个时,遍历完成,退出 
	//不能在 while 里写这条判断!不然会少遍历一个点 
	cur=edge[cur].nxt;//将当前节点赋值为当前节点的下一个 
}

for 循环也能轻松实现,如下:

for(int i=_head[u];i!=-1;i=edge[i].nxt){
	//do something
}

完整代码如下

#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
ll read(){
	ll x=0,f=1;
	char c=getchar();
	while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
const int M=1e6+5;
const int N=1e4+5;
struct{
	int v;
	int w;
	int nxt;
}edge[M*2];
int _head[N];
int e_cnt=-1;
void add(int u,int v,int w){
	e_cnt++;
	edge[e_cnt].v=v;
	edge[e_cnt].w=w;
	edge[e_cnt].nxt=_head[u];
	_head[u]=e_cnt;
} 
int main(){
	int n,m;
	cin>>n>>m;
	memset(_head,-1,sizeof _head);	
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add(u,v,1);
		add(v,u,1); 
	}	
	
	//do something
	
	return 0;
}


迁移自洛谷

posted @ 2025-02-04 12:06  hm2ns  阅读(129)  评论(0)    收藏  举报