图的存储

当给定一张图后,我们需要存储。下面有三种存储方法。

1. 邻接矩阵

直接用二维数组 \(g_{i,j}\) 来表示从结点 \(i\) 到结点 \(j\) 的距离。

空间复杂度为 \(\mathcal{O}(N^2)\)

2. 邻接表

可以发现,邻接矩阵在存稀疏图时,有大量的空间浪费。此时可以使用邻接表存图。

邻接表就是对于每个结点,只记录它的出边。这样能快速访问一个点的所有出边,但不能快速访问一条指定的出边。

一般用 vector 实现。

空间复杂度为 \(\mathcal{O}(M)\)

const int N=1e5+5;
struct EDGE{
	int v,w;
	EDGE(){}
	EDGE(int V,int W){
		v=V;w=W;
	}
};
vector<EDGE> g[N];

3. 链式前向星

将邻接表改用静态数组,就有了链式前向星。

邻接表存图的方式为每次加入边后,由最后一条边指向它。而链式前向星是由表头指向它,再由它指向原来的第一条边。

\(hd_u\) 表示结点 \(u\) 的出边中第一条边。每次加边后,用 \(cnt\) 表示新加的边的编号。每条边有三个参数:下一条边的编号、指向的点与边权。于是就有了以下结构体:

struct EDGE{
	int nx,to,vl;
	EDGE(){}
	EDGE(int NX,int TO,int VL){
		nx=NX;to=TO;vl=VL;
	}
	/*nx为下一条边的编号,to为指向的点,vl为边权*/
};

如果要增加一条由 \(u\) 指向 \(v\),边长为 \(w\) 的边时,只需要让 \(nx \leftarrow hd_u,to \leftarrow v,vl \leftarrow w\),这条边就存好了。接下来让 \(hd_u \leftarrow cnt\) 就大功告成了。此时 \(hd_u\) 指向了这条边,而这条边又指向了原来的 \(hd_u\),即原来的第一条边。

void add(int u,int v,int w){
	ed[++cnt].nx=hd[u];
	ed[cnt].to=v;
	ed[cnt].vl=w;
	hd[u]=cnt;
}

如果要遍历点 \(u\) 的所有出边,只需要先让 \(i \leftarrow hd_u\),此时 \(i\) 指向了 \(u\) 的第一条出边。接下来不断让 \(i\) 变为 \(ed_i \rightarrow nx\),即 \(u\) 出边的下一条边即可。

for(i=hd[u];i;i=ed[i].nx){
	v=ed[i].to;w=ed[i].vl;
//	这里遍历的是点u能到达的所有点,v是到达的点的编号,w是边权
}

空间复杂度为 \(\mathcal{O}(M)\),在稀疏图中效率很高。

例1 洛谷-B3643

本体思路:使用邻接矩阵和邻接表存图,还要对邻接表进行排序。

代码

posted @ 2024-02-08 20:01  lrx139  阅读(33)  评论(0编辑  收藏  举报