一、图的存储
三种数据结构:
邻接矩阵
1. 定义:邻接矩阵是一个二维数组,其中`matrix[i][j]`表示顶点i到顶点j是否有边。对于无向图,这个矩阵是对称的。
2. 优点:
- 简单直观。
- 方便检查任意两个顶点间是否存在边。
- 适合表示稠密图,因为其空间复杂度与图的大小无关。
3. 缺点:
- 空间复杂度高,为O(V^2),其中V是顶点数。
- 不适合表示稀疏图,因为大量的空间被浪费在表示不存在的边上。
- 遍历邻接点的效率低(需要遍历整行或整列)。
邻接表
1. 定义:邻接表是一个数组,其中每个元素是一个链表,链表中的每个节点代表一个顶点的邻接顶点。
2. 优点:
- 空间复杂度相对较低,为O(V + E),其中E是边数。
- 适合表示稀疏图。
- 便于遍历任一顶点的所有邻接点。
3. 缺点:
- 检查两个顶点间是否存在边相对较慢。
- 相比邻接矩阵,实现稍微复杂。
链式前向星
1. 定义:链式前向星是邻接表的一种优化实现,通过两个数组来实现。一个存储边的信息(如终点和权重),另一个存储每个顶点的边表头部。
2. 优点:
- 高效地存储和遍历图的边。
- 空间效率和邻接表相似,适用于稀疏图。
- 适合于边数远大于点数的图。
3. 缺点:
- 实现复杂度高于邻接表。
- 检查两个顶点间是否存在边的效率较低。
总结
- 邻接矩阵适用于表示稠密图,简单但空间效率低。
- 邻接表适用于表示稀疏图,空间效率较高,但查找边的效率较低。
- 链式前向星是邻接表的优化版,适用于边数远大于点数的图,实现较为复杂。
链式前向星代码:
创建
struct Edge
{
int to, w, next; // to - 目标节点, w - 权重, next - 下一个边
}
Edge[M * 2]; // 边的数组,大小是边的数量的两倍
int head[N]; // 头节点数组
int tot, n, m; // tot - 总边数当前索引, n - 节点数, m - 边数
void addedge(int u, int v, int w) // u - 起点, v - 终点, w - 权重
{
edge[++tot].to = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot; // 新增边的头节点是u
}
}
遍历
for(int i=head[x];i;i =edge[i].next){
...
}
特点: 存各种图都很适合,但不能快速查询一条边是否存在,也不能方便地对一个点的出边进行排序。
优点是边是带编号的,有时会非常有用,而且如果 cnt 的初始值为奇数,存双向边时 i ^ 1 即是 i 的反边
在一些对空间严格的题目上也可以使用,比邻接表vector写法的空间更好