邻接表
作为一名现役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;
}