【学术篇】浅谈各种邻接表

来源:互联网 发布:windows私有云软件 编辑:程序博客网 时间:2024/06/05 23:55

首先注:下面出现的代码完全没经编译,不保证编译成功,大家当伪代码看较好~

在OIer的世界里,有一种奇妙的东西,叫图论。。
而对于计算机,我们并不能将一张图输入,而需要一些存图方式
比如下面这张图(画得不好看勿喷~):
这里写图片描述
最简单的存图方式是邻接矩阵
*在一个n*n的矩阵中,第(i,j)个格子的值表示从i到j这条边的边权。。
上图用邻接矩阵表示就是:

- 0 1 2 3 4 5 6 0 0 3 0 5 0 0 0 1 3 0 2 0 0 0 0 2 0 2 0 2 0 0 0 3 5 0 2 0 3 5 0 4 0 0 0 3 0 2 0 5 0 0 0 5 2 0 8 6 0 0 0 0 0 8 0

图的储存结构:

int tu[N][N]; 

建边:

inline void build(int x,int y,int z){ //建从x到y边权为z的边    tu[x][y]=z; //单向边#ifdef DOUBLE_EDGE    tu[y][x]=z; //双向边#endif}

枚举边:

int x=start;for(int i=0;i<n;i++)    if(tu[x][i]){ //若不为0表示相连        int y=i; //跑到i的点    }

这个东西有个显而易见的缺点,O(n^2)的效率就决定了无法处理任何大规模的数据。。
而O(nlogn)的算法中的n基本都在10^5级别。。数组都开不开啊_ (:з」∠) _

所以我们需要邻接表,这样时间、空间效率都被控制在O(m)级别(m为边数)
正统的邻接表是用指针……
邻接表对于每个点主要保存了以下东西:
- 这个点出发的第一条边
- 每条边(和下面的都指该点出发的边)所指向的点
- 每条边的边权(data域)
- 每条边指向的下一条边
储存的结构基本上是这样的:

struct edge{    int to,data;    etype *next,*pair; //pair为反向边,视情况而建    edge(){}    edge(int t,int d,edge* nxt):to(t),data(d),next(nxt){}    //dalao们发现C++的new实在是太慢了,所以要自己写一个orz    void* operator new(unsigned,void* p){return p;}}*e[N];/*还有一个*/int Pe=0; //记录了边的总数

建边:

void build(int x,int y,int z){    e[x]=new(Pe++)edge(y,z,e[x]); //指向y,边权z,上一条边是上一个e[x]..#ifdef DOUBLE_EDGE    e[y]=new(Pe++)edge(x,z,e[y]);    e[x]->pair=e[y];    e[y]->pair=e[x];#endif}

枚举边:

int x=start;for(etype *i=e[no];i;i=i->next)    int y=i->to;

嗯,很好,我们现在有了存图的利器
但是,指针的特性决定了对我们精确的要求。。
不禁想起了指针调试的恐惧
所以,我们应该是有更简(ju)便(ruo)的方法的:
比如,数组模拟……(这应该也是应用最广泛的吧……)
于是存储结构变成了:

struct edge{    int to,next,data;}e[M]; int v[N],tot=0; //v数组就是很多很多dalao的first数组,存点i出发的第一条边备查//网络流图中tot为了异或找反向边容易可以置为1..

建边:

void build(int x,int y,int z){    e[++tot].to=y; e[tot].next=v[x]; e[tot].data=z; v[x]=tot;#ifdef DOUBLE_EDGE    e[++tot].to=x; e[tot].next=v[y]; e[tot].data=z; v[y]=tot;#endif}

枚举边:

int x=start;for(int i=v[x];i;i=e[i].edge)    int y=e[i].to;

对就是这样。。
其实似乎写到这里就可以结束了。。
但是其实还是有一种写法的。。
比如,C++的盆友们,还记得你们的STL么?
vector也可以用来存邻接表哦(下面我给出没有边权的)~
(而且似乎省选开O2什么的STL会格外跑得快)
存储结构?

vector<int> e[N];

建边:

void build(int x,int y){    e[x].push_back(y); //媲美邻接矩阵的简便..#ifdef DOUBLE_EDGE    e[y].push_back(x);#endif}

枚举边:

int x=start;for(/*unsigned*/int i=0;i<e[x].size();i++){ //不想要warning的可以加unsigned..    int y=e[x][i];}

行了,就介绍这么多。。。
希望对大家有帮助,希望大家用的开心~~

0 0