图的存储与搜索
图的存储
存储一般使用邻接矩阵和邻接表两种方式。邻接矩阵中,g[i][j]
表示节点i指向节点j的一条有向边。邻接矩阵需要O(n * n)的空间,因此我们常使用邻接表。
ps:一般稠密图用邻接矩阵,稀疏图用邻接表。稠密图中边的数量是节点个数的次方,也就是边数量远远大于节点数量。
邻接表是数组+链表的形式。比如节点1-6,那么h[1]就表示节点1直接连接的节点,h[1]指向一条链表。
代码中一般这样写:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10, M = N * 2;
int h[N], e[M], ne[M], idx;
bool st[N];
int n;
// 添加一条边,头插法加入链表
int add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int main() {
memset(h, -1, sizeof h);
scanf("%d", &n);
// 加入n条无向边,无向边可以看作两个节点互相指向的有向边
for (int i = 0; i < n; i ++ ) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
return 0;
}
图的搜索
dfs是深度优先搜索,借助语言本身的递归栈实现;bfs是广度优先搜索,常使用queue实现。dfs一般都用来求一些通用性质的值,比如求子树节点个数。bfs是层层向外寻找,具备最短路的性质,在边的权重是1时,可以用作寻找最短路。
由于使用邻接表存储图,在搜索时遍历到链表的某个节点a,就找到a所连接的链表,由此会有死循环问题,所以需要使用数组记录节点是否已经被使用过。
常用模版:
void dfs(int u) {
st[u] = true;
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (!st[v]) {
dfs(v);
}
}
}
void bfs() {
// 初始化队列
memset(d, -1, sizeof d);
int hh = 0, tt = -1;
q[ ++ tt] = 1;
while (hh <= tt) {
int t = q[hh ++ ];
st[t] = true;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j] == -1) {
// 把满足要求的节点放入队列
q[++ tt] = j;
}
}
}
}
参照例题:
https://www.acwing.com/problem/content/848/
https://www.acwing.com/problem/content/849/