树与图的存储

「存储方式:」

  • 邻接矩阵

  • 邻接表(常用)

    1
    2
    3
    4
    5
    6
    //邻接表
    int h[N], e[N * 2], ne[N * 2], idx;

    void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }

树与图的深度优先遍历

遍历就根据邻接表遍历方式进行。

1
2
3
4
5
6
7
8
9
10
11
--------模版---------------
// 需要标记数组st[N], 遍历节点的每个相邻的便
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);
}
}
}

树的重心

给定一颗树,树中包含 n个结点(编号 1∼n)和 n−1条无向边。

请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式:第一行包含整数 n,表示树的结点数。

接下来 n−1行,每行包含两个整数 a和 b,表示点 a和点 b之间存在一条边。

输出格式:输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。

数据范围
1≤n≤105
输入样例:

1
2
3
4
5
6
7
8
9
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6

输出样例:

1
4

「实现思路:」

从第一个点出发,在每个点上进行统计操作,去除该点后,统计剩余的连通块数量的最大值,然后在最大值值里面选择最小的。

「完整代码:」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 10; //数据范围是10的5次方
const int M = 2 * N; //以有向图的格式存储无向图,所以每个节点至多对应2n-2条边

int h[N]; //邻接表存储树,有n个节点,所以需要n个队列头节点
int e[M]; //存储元素
int ne[M]; //存储列表的next值
int idx; //单链表指针
int n; //题目所给的输入,n个节点
int ans = N; //表示重心的所有的子树中,最大的子树的结点数目

bool st[N]; //记录节点是否被访问过,访问过则标记为true

//a所对应的单链表中插入b a作为根
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
//返回以u为根的子树中节点的个数,包括u节点
int dfs(int u) {
int res = 0; //存储 删掉某个节点之后,最大的连通子图节点数
st[u] = true; //标记访问过u节点
int sum = 1; //存储 以u为根的树 的节点数, 包括u,如图中的4号节点

//访问u的每个子节点
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
//因为每个节点的编号都是不一样的,所以 用编号为下标 来标记是否被访问过
if (!st[j]) {
int s = dfs(j); // u节点的单棵子树节点数 如图中的size值
res = max(res, s); // 记录最大联通子图的节点数
sum += s; //以j为根的树 的节点数
}
}

//n-sum 如图中的n-size值,不包括根节点4;
res = max(res, n - sum); // 选择u节点为重心,最大的 连通子图节点数
ans = min(res, ans); //遍历过的假设重心中,最小的最大联通子图的 节点数
return sum;
}

int main() {
memset(h, -1, sizeof h); //初始化h数组 -1表示尾节点
cin >> n; //表示树的结点数

// 题目接下来会输入,n-1行数据,
// 树中是不存在环的,对于有n个节点的树,必定是n-1条边
for (int i = 0; i < n - 1; i++) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a); //无向图
}

dfs(1); //可以任意选定一个节点开始 u<=n

cout << ans << endl;

return 0;
}

树与图的广度优先遍历

「模版:」

用队列进行广度优先遍历,然后遇到了就打标记,统计该点能去的其他位置点,之后统计结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int e[N], ne[N], h[N], idx;
queue<int> q1;
void bfs(int x)
{

q1.push(x);
res_arr[x] = 0;

while (!q1.empty())
{
int j = q1.front(); // q1.front() 是 idx,即元素下标,e[q1.front()] 是元素本身
q1.pop();
// i 指的是 idx 下标
for (int i = h[j]; i != -1; i = ne[i])
{

int e_num = e[i];
if (!st[e_num])
{
..................................
}
}
}

}

「题目:」

图中点的层次

给定一个 n 个点 m条边的有向图,图中可能存在重边和自环。

所有边的长度都是 1,点的编号为 1∼n。

请你求出 1号点到 n号点的最短距离,如果从 1号点无法走到 n号点,输出 −1。

输入格式:
第一行包含两个整数 n和 m。

接下来 m 行,每行包含两个整数 a和 b,表示存在一条从 a走到 b的长度为 1 的边。

输出格式:
输出一个整数,表示 1号点到 n 号点的最短距离。

数据范围
1≤n,m≤105
输入样例:

1
2
3
4
5
6
4 5
1 2
2 3
3 4
1 3
1 4

输出样例:

1
1

「解题思路:」

利用广度优先搜索,把第一次搜到的该点进行标记与距离统计标记,之后把当前点能前往的点push进队列,再进行循环操作。

「完整代码:」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;

int e[N], ne[N], h[N], idx;
int res_arr[N];

bool st[N];
queue<int> q1;
int n, m;

void add(int a, int b)
{

e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}

void bfs(int x)
{

q1.push(x);
res_arr[x] = 0;

while (!q1.empty())
{
int j = q1.front(); // q1.front() 是 idx,即元素下标,e[q1.front()] 是元素本身
q1.pop();
// i 指的是 idx 下标
for (int i = h[j]; i != -1; i = ne[i])
{

int e_num = e[i];
if(e_num == j)
{
continue;
}
if (!st[e_num])
{
q1.push(e_num);
st[e_num] = true;
res_arr[e_num] = res_arr[j] + 1;
}
}
}
if (res_arr[n] == -1)
{
cout << -1 << endl;
}
else
{
cout << res_arr[n] << endl;
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
memset(res_arr,-1,sizeof res_arr);
while (m--)
{
int a, b;
cin >> a >> b;
add(a, b);
}

bfs(1);
return 0;
}