1.树与图的存储
(1)邻接矩阵
(2)邻接表
// 链式前向星模板(数组模拟) #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 100010, M = N * 2; int h[N], e[M], ne[M], idx; // 头,边,next值;n个单链表,所以有n个头h[N] void add(int a, int b) { // 在头为a的表中头插b(此时编号为idx) e[idx] = b; ne[idx] = h[a]; h[a] = idx; idx++; } int main() { memset(h, -1, sizeof h); }
2.树与图的遍历
(1)深度优先遍历DFS
// 数和图的DFS模板 #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 100010, M = N * 2; int h[N], e[M], ne[M], idx; bool st[N]; void add(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx; idx++; } void dfs(int u) { st[u] = true; // 标记已被搜过 for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; if (!st[j]) dfs(j); } } int main() { memset(h, -1, sizeof h); dfs(1); }
树的重心
给定一颗树,树中包含 n 个结点(编号 1∼n)和 n−1条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 n,表示树的结点数。
接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。
输出格式
输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
1≤n≤105
输入样例
9 1 2 1 7 1 4 2 8 2 5 4 3 3 9 4 6
输出样例:
4
思路:删掉某一个点,此点的孩子各成一个连通块(有几个孩子就有几个连通块),整棵树除去此点及其孩子成为一个连通块
// 树的重心 #include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 100010, M = N * 2; int h[N], e[M], ne[M], idx; bool st[N]; int ans = N; int n; void add(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx; idx++; } // 返回以u为根的子树里点的数量 int dfs(int u) { st[u] = true; // 标记已被搜过 int sum = 1, res = 0; // sum:当前子树大小(此时为要删的节点,所以为1);res:删掉点后,连通块的最大值 for (int i = h[u]; i != -1; i = ne[i]) { int j = e[i]; // e[]:一条边链接i和j(=e[i]) if (!st[j]) { int s = dfs(j); // 子树里点的数量 res = max(res, s); sum += s; // 当前子树大小(=自己+子树) } } res = max(res, n - sum); // 树中除了节点及其子树以外的点,它们构成一个连通块 ans = min(ans, res); return sum; } int main() { cin >> n; memset(h, -1, sizeof h); for (int i = 0; i < n; i++) { int a, b; cin >> a >> b; add(a, b); add(b, a); // 无向图 } dfs(1); cout << ans << endl; return 0; }
(2)广度优先遍历DFS
图中点的层次
给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环。
所有边的长度都是 1,点的编号为 1∼n。
请你求出 1 号点到 n 号点的最短距离,如果从 1 号点无法走到 n 号点,输出 −1。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 a 和 b,表示存在一条从 a 走到 b 的长度为 1 的边。
输出格式
输出一个整数,表示 1 号点到 n 号点的最短距离。
数据范围
1≤n,m≤105
输入样例:
4 5 1 2 2 3 3 4 1 3 1 4
输出样例:
1
// 图中点的层次 #include <iostream> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N = 100010, M = N * 2; int h[N], e[M], ne[M], idx; int d[N], q[N]; int n, m; void add(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } int bfs() { queue<int> q; q.push(1); memset(d, -1, sizeof d); // 初始化距离d d[1] = 0; while (!q.empty()) { int t = q.front(); q.pop(); for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; if (d[j] == -1) { d[j] = d[t] + 1; q.push(j); } } } return d[n]; } int main() { cin >> n >> m; memset(h, -1, sizeof h); for (int i = 0; i < m; i++) { int a, b; cin >> a >> b; add(a, b); } cout << bfs() << endl; return 0; }
3.BFS的应用:拓扑排序
有向图的拓扑序列
给定一个 n 个点 m 条边的有向图,点的编号是 1 到 n,图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1−1。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 n 和 m。
接下来 m 行,每行包含两个整数 x 和 y,表示存在一条从点 x 到点 y 的有向边 (x,y)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 −1。
数据范围
1≤n,m≤105
输入样例:
3 3 1 2 2 3 1 3
输出样例:
1 2 3
// 拓扑序列 #include <iostream> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N = 100010, M = N * 2; int h[N], e[M], ne[N], idx; int in[N]; // 入度 int n, m; void add(int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } int ans[N]; bool toposort() { int id = 0; queue<int> q; for (int i = 1; i <= n; i++) // 注意是1~n而不是0~n-1 { if (in[i] == 0) { q.push(i); } } while (!q.empty()) { int t = q.front(); q.pop(); id++; ans[id] = t; // 记录拓扑序 for (int i = h[t]; i != -1; i = ne[i]) { int j = e[i]; in[j]--; if (in[j] == 0) { q.push(j); } // 之后不要in[j]++; } } if (id < n) return false; else return true; } int main() { cin >> n >> m; memset(h, -1, sizeof h); for (int i = 0; i < m; i++) { int x, y; cin >> x >> y; add(x, y); in[y]++; } if (toposort()) { for (int i = 1; i <= n; i++) { cout << ans[i] << " "; } } else { cout << -1; } return 0; }