图的遍历
常见的图的遍历方式:
- DFS:时间复杂度
- BFS:时间复杂度
cpp
DFS(u) // u 图中的一个顶点。
访问 u
在 u 上打访问标记
for v in u 的相邻节点
if v 未被访问 then
DFS(v)
end
end
end
BFS(u)
访问 u
u 标记访问
u 入队
while 队列不为空
h <- 队头出队
for v in h 的相邻节点
if v 未被访问 then
v 入队
v 标记访问
end
end
end
end图的 DFS 遍历——树的重心
题目描述
给定一颗树,树中包含
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数
接下来
输出格式
输出一个整数
数据范围
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6输出样例
4参考代码
cpp
// 使用vector存储图
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int ans = N;
int st[N];
vector<vector<int>> g(N);
int dfs(int u) { // 返回以u为根的子树的节点数目
st[u] = 1;
int size = 0, sum = 1;
for (int i = 0; i < g[u].size(); i++) {
int j = g[u][i];
if (!st[j]) {
int s = dfs(j);
size = max(size, s);
sum += s;
}
}
size = max(size, n - sum);
ans = min(ans, size);
return sum;
}
int main() {
cin >> n;
for (int i = 1; i <= n - 1; i++) {
int a, b; cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1);
cout << ans << endl;
return 0;
}
// 使用邻接表存储图
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 2 * N;
int h[N], e[M], ne[M], idx;
int n;
int ans = N;
int st[N];
void init() {
memset(h, -1, sizeof h);
idx = 0;
}
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int dfs(int u) { // 返回以u为根的子树的节点数目
st[u] = 1;
int size = 0, sum = 1;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]) {
int s = dfs(j);
size = max(size, s);
sum += s;
}
}
size = max(size, n - sum);
ans = min(ans, size);
return sum;
}
int main() {
init();
cin >> n;
for (int i = 1; i <= n - 1; i++) {
int a, b; cin >> a >> b;
add(a, b);
add(b, a);
}
dfs(1);
cout << ans << endl;
return 0;
}图的 BFS 遍历——图中点的层次
题目描述
给定一个
所有边的长度都是
请你求出
输入格式
第一行包含两个整数
接下来
输出格式
输出一个整数,表示
数据范围
输入样例
4 5
1 2
2 3
3 4
1 3
1 4输出样例
1参考代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int h[N], e[N], ne[N], idx;
int q[N];
int d[N];
int n, m;
void init() {
memset(h, -1, sizeof h);
idx= 0;
}
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int bfs() {
int hh = 0, tt = -1;
q[++tt] = 1;
memset(d, -1, sizeof d);
d[1] = 0;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (d[j] == -1) {
q[++tt] = j;
d[j] = d[t] + 1;
}
}
}
return d[n];
}
int main() {
init();
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b; cin >> a >> b;
add(a, b);
}
cout << bfs() << endl;
return 0;
}利用图的遍历求连通分量的个数
问题描述
警察抓到了
输入描述
第一行输入一个整数
第二行输入一个整数
之后
输出描述
一个整数,表示犯罪团伙的数量。
输入样例
11
8
1 2
4 5
3 4
1 3
5 6
7 10
5 10
8 9输出样例
3问题分析
从子图中的任意顶点出发,可以将子图中的顶点逐一遍历并进行标记,子图遍历结束即找到一个连通分量。再从下一个未标记顶点出发进行遍历,依次类推。直到所有顶点都被遍历。
参考代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 5 * N;
int h[N], e[M], ne[M], idx;
int st[N];
int n, m;
void init() {
memset(h, -1, sizeof h);
idx = 0;
}
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs(int u) {
st[u] = 1;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]) {
dfs(j);
}
}
}
int main() {
init();
cin >> n >> m;
int a, b;
for (int i = 1; i <= m; i++) {
cin >> a >> b;
add(a, b);
add(b, a);
}
int cnt = 0;
for (int i = 1; i <= n; i++) {
if (!st[i]) {
cnt++;
dfs(i);
}
}
cout << cnt << endl;
return 0;
}