之前一直使用vector存储邻接表,但是由于一本通疯狂使用数组模拟的邻接表,加上出现了一些题目使我难受。特此整理一下这种存储图的方式。
1.前向星是怎么存图的
一种数据结构,以储存边的方式来存储图。构造方法如下:读入每条边的信息,将边存放在数组中,把数组中的边按照起点顺序排序,前向星就构造完了。通常用在点的数目太多,或两点之间有多条弧的时候。一般在别的数据结构不能使用的时候才考虑用前向星。除了不能直接用起点终点定位以外,前向星几乎是完美的。
—— 百度百科
使用vector存图的时候我们是通过保存邻接点,来实现图的存储。
是这个亚子的:
前向星保存的却是边,将边编上号,然后保存下来,-1表示没有边了 和 null的意义一致。
是这个亚子:
2.链式前向星
Ⅰ.结构体
struct Edge{
int to;//第i条边的终点
int nex;// 表示与第i条边同起点的下一条边的存储位置
int w;//边的权值
};
Ⅱ.edge[]数组和head[]数组
edge[]数组
保存边的边集数组。
edge[i]保存的就是编号为i的边信息,edge[i].to表示第i条边的终点,edge[i].next表示与第i条边同起点的下一条边的存储位置,edge[i].w为边权值。
head[]数组
head[i]表示以i为起点的第一条边存储的序号,实际上你会发现这里的第一条边存储的序号其实
就是以i为起点的所有边的最后输入的那个编号。
加边代码
//表示添了一条从u指向v的有向边,若是无向图就把u和v反过来,再添一遍
void addedge(int u,int v,int w){
edge[++cnt_edge].nex = head[u];
edge[cnt_edge].to = v;
//eage[cnt_edge].w = w;
head[u] = cnt_edge;
}
来模拟一下,以上面出现的图为例,输入边:
1 3
因为无向图,相当于又加了一个(3,1)的有向边,下面也是一样。
4 3
3 6
6 2
到此模拟结束。
Ⅲ.遍历
因为我们之前提到过:head[i]就是以i为起点的第一条边存储的序号,这里的第一条边存储的序号其实就是以i为起点的所有边的最后输入的那个编号。
所以使用前向星遍历的时候,我们其实是从最后一条边倒着找到所有和i相邻的边的,通过head[i]找到与i相邻的第一条边,然后按着nex的顺序一直找到nex[j] = -1,也就是没有和点i相邻的边为止。
就拿上边的点3,举个例子:
-
head[3] = 5,即最后一个与点3相邻的边是edge[5](3,6)。
-
edge[5].nex = 4,证明前一个和5号边同一个起始点的边是4号边(3,4),所以我们下一个访问的边就是4号边。
-
edge[4] .nex = 2,证明前一个和4号边同一个起始点的边是2号边(3,1),所以我们下一个访问的边就是2号边。
-
edge[2].nex = -1,证明2号边之前没有和2号边同起点的边了。
到此,遍历了所有与点3相邻的边。
代码
for(int i = 1; i <= 2*m; i++){
for(int j = head[i]; j != -1; j = edge[j].nex){
printf("%d,%d →",i,edge[j].to);
}
printf(" -1\n");
// printf("head %d : %d\n",i,head[i]);
}
3.完整代码
#include <bits/stdc++.h>
using namespace std;
const int Max = 1e4;
struct Edge{
int to;//第i条边的终点
int nex;// 表示与第i条边同起点的下一条边的存储位置
int w;//边的权值
}edge[Max];
int head[Max];
int n,m,cnt_edge=0;
void addedge(int u,int v,int w){
edge[++cnt_edge].nex = head[u];
edge[cnt_edge].to = v;
//eage[cnt_edge].w = w;
head[u] = cnt_edge;
}
int main(){
scanf("%d %d",&n,&m);
int tmp = m;
memset(head,-1,sizeof(head));
while(tmp--){
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v,1);
addedge(v,u,1);
}
for(int i = 1; i <= 2*m; i++){
printf("%d : ",i);
for(int j = head[i]; j != -1; j = edge[j].nex){
printf(" %d,%d →",i,edge[j].to);
}
printf(" -1\n");
// printf("head %d : %d\n",i,head[i]);
}
return 0;
}
/*
6 4
1 3
4 3
3 6
6 2
*/