【图论】关于邻接表建图

邻接表
作为一名现役Oier在做过无数图论题目和搜索题目之后,可以说是相当熟悉了,但是今天做的一道题,让我对于邻接表表示直接懵逼,原来在跑最短路的时候还是明白每个数组各自的作用的,然而一旦在其它题目中遇到就不会了,所以今天特意复习,并决定好好整理一下,以后别再懵逼了…


附上神犇ahalei的Blog:http://blog.csdn.net/ahalei/article/details/23356781
(里面对于邻接表的解释还是很透彻的)


邻接表

但什么是邻接表呢(・ω・。)ʃฺ??

众所周知,邻接矩阵是用矩阵实现存图的一类数据结构
而邻接矩阵,则是用链表(好多个)来实现存图的一类数据结构
但是手打链表mdzz,指针都不怎么用啊(一直都是在用数组下标模拟)
所以说对于邻接表我们也可以用数组来模拟实现

代码(结构体封装)

int head[maxn];

struct node{
    int from,to,cost,next;
}e[maxn << 1];//无向图(maxn << 1)

初始化

for(int i = 1;i <=n;i++){//n由题意而来
    head[i] = -1;
}

//当然也可以一步写成memset(head,-1,sizeof(head)),但是实测比for一遍更慢

这里,head 数组存的是我们建图时,某个点所存的最后一条边的编号

例如我们存入点1的一条边,这条边是总第5条边,则我们将head[1]赋值为5,但是之前我们1号点若已经记录过一条边,如果我们这时修改head[1]那么之前那条边不就没了吗,但是显然我们不能让这条边消失,而是应该想办法让它在head修改之后依然能够被找到

这时候别忘了,我们用的可是链表啊,这时候就要用到我们所建结构体中的next,从next字面意思上就可以理解,即为head所记录边的“下一条”边,简单地说就是记录当前head[i]所存边之前,head[i]所存的那条边,但是如果我们再像head一样存边是不可能的了,那么我们next应该存什么呢?我们在e[i].next中应该存的是记录第i条边前,所记录的是第几条边( 即这条边的编号 )

例如我们存入1号点的第5条边之前,head[1] = 3,即当前由1发出的边是总数的第3条边,这时令head[1] = 5,再令e[5].next = 3,意思是从第五条开始的 所指向的下一条边是第3号边,这样一来就保证了每条被记录的边都可以被找到且记录上不会冲突,同时因为第5条边是从1号点发出的,同时第五条边也仅仅指向第3条边,这就保证了我们当前所访问的点都是从1号点发出的,由head可以知道发出点是哪一个。

代码建边:

void build(int f,int t,int c){
    tot++;//当前记录的是总数第几条边,因为正在加边所以tot++
    e[tot].from = f;//在很多情况下这一步其实是不必要的,因为head已记录
    e[tot].to = t;
    e[tot].cost = c;//在图有权值的情况下
    e[tot].next = head[f];//这一步就是上文所述的内容
    head[f] = tot;
}

同时因为还记录了from和to,且from和to所代表的点记录时顺序编号相同并且编号唯一(对于第i条边,它的两端点编号一定是i,当然一个点可以存在于多条边上,所以可以被记录多次,但是在访问某一条边的时候是不会冲突的(废话太多233))所以知道了编号也就可以知道这个点经过这条边所指向的点的是谁(e[i].to)

因此可以想到如何到遍历一个点所联通的所有边
即从head[i]一直遍历到该点所存的第一条边(例如第一条存的就是第一条边,即e[1].next,当我们扫到以该边起始的第一条边的时候,就会得到e[].next == -1了,这时候结束循环即可)

代码如下

//遍历1号节点连接的边
int k = head[1];
for(int i = k;i != -1;i = next[i]){
    //do something...
}

在实际应用中邻接表建图更加适用于稀疏图(边数较少的图)
但当边数较多的时候用邻接表建图便不够优秀了,因为它用的是链表存图,当你要查询一条很早之前记录的边的时候,你要将这条边之后所有记录过的边都遍历一遍,用时相当之慢,还不如邻接矩阵(但邻接矩阵当点太多的时候,n^2空间复杂度无法接受)

最后放上CODE[VS]1557 热浪的代码来体现邻接表在应用中的特点

题目地址(裸的最短路模板题)http://codevs.cn/problem/1557/

代码如下(SPFA+SLF优化)

/*
作者:Loi_Peacefuldoge
题目:p1557 热浪
*/

#include 
#include 
#include 
#include 
#include 
#include 

const int maxn = 100005; 

using namespace std;

int tot;
int t,c,ts,te; 
int dist[maxn];
int head[maxn];
bool used[maxn];

struct node
{
    int f,t,c,next;
}es[maxn << 1];

inline void rd(int &x)
{
    scanf("%d",&x);
} 

inline void build(int x,int y,int z)
{
    tot++;
    es[tot].f = x;
    es[tot].t = y;
    es[tot].c = z;
    es[tot].next = head[x];
    head[x] = tot;
}

deque<int >q;

inline void spfa(int be)
{
    memset(dist,0x3f,sizeof(dist));
    while(!q.empty())q.pop_front();
    q.push_back(be);
    used[be] = true;
    dist[be] = 0;
    while(!q.empty())
    {
        int u = q.front();
        used[u] = false;
        q.pop_front();
        for(int i = head[u];i;i = es[i].next)
        {
            int v = es[i].t;
            if(dist[v] > dist[u] + es[i].c)
            {
                dist[v] = dist[u]+es[i].c;
                if(!used[v])
                {
                    if(dist[v] < dist[q.front()]) q.push_front(v);
                    else q.push_back(v);
                    used[v] = true; 
                }
            }
        }
    }
}

void init()
{
    memset(used,0,sizeof(used));
    memset(head,0,sizeof(head));
    tot = 0;
    return ;
}

int main()
{
    init();
    rd(t);rd(c);rd(ts);rd(te);
    for(int i = 1;i <= c;i++)
    {
        int x,y,z;
        rd(x);rd(y);rd(z);
        build(x,y,z);
        build(y,x,z);
    }
    spfa(ts);
    printf("%d\n",dist[te]);
return 0;
}

你可能感兴趣的:(【NOIP2016】,【图论-最短路问题】,【图论-最小生成树问题】,【图论-综合】,【数据结构-综合】)