图的存储——邻接表

来源:互联网 发布:济宁网络问政平台首页 编辑:程序博客网 时间:2024/05/29 04:22

说明

在许多图论的题中,存储图是必不可少的,这里就来说说我平时存储图的方式,与大家交流交流 罒V罒

容器

# 说明 num 记录到现在已经存了多少条边了(在添加边的时候会用到) head[x] 以 x 为入点的某一条边的编号 .to 第 x 条边的下一条边的编号(可以把它想象为一个通往下一个边的链接) .v 这一条边的出点(入点当然还是开始的那个head[x]中的x) .w 边权值(不需要的话可以不要,根据题目也可以再加几个参数)
int head[N],num;struct edge{    int to,v,w;} e[N];

添加边

  • 看看程序和注释,感受一下吧
void add(int u,int v,int w){    num++;              //新的一条边     e[num].v=v;         //处理一下这条边的信息     e[num].w=w;    e[num].to=head[u];  //把 当前 head[u] 那条边接到 num 后面     head[u]=num;        //把这条边接到 u 的 head[] 上 }

枚举边

  • 当我们我们需要枚举所有以某个点(这里就当成是 x 吧)为入点的边,就可以用到下面的程序
  • 由于起初 e[].to 都是 0 (当然我是这样的,你们喜欢也可以初始化成-1),所以就用 e[].to 是否为0来判断判断是否还有边。
  • 这个代码是为了理解才这么长,最后提示中有个不错的宏定义,平时就用那个,又快又方便!
for (int i=head[x],o; i!=0; i=e[i].to){    o=e[i].v;    //这里就可以做各种处理操作    printf("%d -> %d (%d)\n",x,o,e[i].w);}

遍历图

  • 要是是个图,那么就用个数组 f[] 来记录某个点是否已经讨论过了(有时按题目需要,可能是用时间戳,不过也差不多……)
  • 代码被我简化了,For(x) 意思就是上面那个枚举边, o 就是枚举出的出点。
void dfs(int x){    f[o]=1;    For(x) if (!f[o]){        //这里可以做各种操作         dfs(o);    }}
  • 要是目标是个树(特殊的图,没有环出现),那么我们就可以省掉 f[] 的空间,每次传个当前节点的父节点 fa 就行了。
  • 原理大概就是……可以自己画个图,感受一下
void dfs(int x,int fa) {    For(x) if (o!=fa){        //这里可以做各种操作        dfs(o,x) ;    }}

提示

  • 有的比赛(比如 codeforces)中,不仅要求你做出题目,完成的速度也是评分的一个要素,那么你就需要好好利用宏定义来简化代码,这样对别人看懂你的代码这方面也有很大帮助。
  • 比如你就可以定义一个 For,让程序更简洁
#define For(x) for (int h=head[x],o=e[h].v; h; o=e[h=e[h].to].v)
  • 还有一点很重要,对于无向边,每次要两边都加一次边(即出入点调换一次再加一次),这样一来,我们存边的数组就得开两倍空间了。
  • 这里有几个用到邻接表的题目,可以看看,实践一下。
  • http://blog.csdn.net/jackypigpig/article/details/69360897
  • http://blog.csdn.net/jackypigpig/article/details/69808594
  • http://blog.csdn.net/jackypigpig/article/details/70227554
0 0
原创粉丝点击