图论存图方式小结
1、邻接矩阵
简单,开一个二维数组,pic[ i ] [ j ] = w 表示标号为 i 的顶点到标号为 j 的顶点有一条权值为 w 的有向边;
初始化:顶点自己到自己距离为0,不存在边设为inf;
2、邻接表
邻接表是一种对于每个顶点,用链表来存储以该点为起点的边的数据结构;
由定义知我们不需要再次记录起点,所以一般用一个结构体来记录边的终点和权值信息,初始化直接对每个顶点clear清空就行了;
struct edge {
int to, w;
edge (int too, int ww) { to = too; w = ww; }
//写不写构造函数无所谓辣,随便把值赋上就行辣 };//指向顶点to的权值为w的边
然后利用stl里的vector来实现链表的功能;
vector <edge> G[MAXN]; void addedge (int u, int v, int w) { G[u].push_back (edge (v, w)); //添加从u到v权值为w的边 }
遍历从某点(v)出发的边;
for (int i = 0; i < G[v].size(); i++) { edge e = G[v][i]; //操作…… }
3、链式前向🌟
1、先说一下前向星;
我们把边按照起点编号升序(如果起点相同就按照终点升序)放到数组中,对于每个顶点,记录下以他们为起点的所有边在数组中的起始位置和这些边的数量,就是所谓的前向星;(注意有的顶点不一定作为起点有边或者干脆就没边)
简单说可以把这个边集数组看成若干块,每一块存储的是以某个顶点为起点的所有边;
通常以 len[ i ] 来记录所有以 i 为起点的边的数量,以head[ i ]记录以 i 为起点的边集在数组中的第一个存储位置,由定义可以看出 head[ 此顶点 ] = head[ 上一个顶点 ] + len[ 上一个顶点 ] ,我们利用这两个数组存储的信息快速索引;len和head数组通常初始化为0和-1;
2、但是这个排序的过程也很耗时,如果用链式前向星,就可以避免排序👇;
如下建立边结构体;
struct Edge{ int next, to, w; }edge[maxn];
链式前向星改了哪呢?为啥叫链式呢?为啥不用排序呢?
看见那个next了🐎?
首先说明,链式前向星是“倒着链”的,也就是说在遍历的时候,是从最后输入的边,遍历回一开始输入的边,这不影响解题,但影响对结构体的理解;
我们同样有一个head[ i ]数组来记录以 i 为起点的“第一条边”的下标,由于是“倒着链”的,很多blog中的“第一条边”其实是输入顺序中,最后一个以该顶点为起点的边的下标;
结构体中:edge[ i ].to 表示第i条边的终点, edge[ i ].next 表示与第i条边同起点的“下一条边”的下标(输入顺序中的“上一条边”), edge[ i ].w为边权;
int cnt=0;//边的下标,或称为存储位置 void add(int u,int v,int w){ edge[cnt].w = w; edge[cnt].to = v; edge[cnt].next = head[u];//和之前的链上 head[u] = cnt++;//更新首位置 }
这样我们就可以很自然的通过head数组顺藤摸瓜走回去了;
for (int i=head[u];~i;i=edge[i].next){ Edge e = edge[i]; //操作…… }
再次强调,存边的顺序并没有对这个图的连通情况和其他任何逻辑结构产生影响,勿要纠结。
参考资料:https://blog.csdn.net/acdreamers/article/details/16902023/
http://jzqt.github.io/2015/07/21/ACM%E5%9B%BE%E8%AE%BA%E4%B9%8B%E5%AD%98%E5%9B%BE%E6%96%B9%E5%BC%8F/