树
下面我们来介绍一般意义上的树,
包括有根树 和 无根树;
有根树基本操作:
vector实现:
1 const int N = 100001;
2
3 VI edges[N + 1];
4 //数组里面的每一个元素都是一个vector,就是用vector来存树,当然也可以用链表来存
5 //edges[i](这是个vector)就代表第i个点的所有子节点的下标
6 int n, father[N + 1];//n代表树里面有多少个节点,father[]代表节点的父节点编号
7
8 void addEdge(int x, int y){
9 edges[x].pb(y);//给x节点添加一个儿子y
10 father[y] = x;//更新y的父节点信息(对于有根树而言)
11 return;
12 }
13
14 void PrintSon1(int x){
15 //遍历x的所有儿子
16 int cntson = edges[x].size();
17 for (int i = 0;i < cntson;i++){
18 cout << edges[x][i] << " ";
19 }
20 return;
21 }
22
23 void PrintSon2(int x){
24 //更简洁的写法
25 for (auto i : edges[x]){
26 cout << edges[x][i] << " ";
27 }
28 return;
29 }
30
31 VI dfn;//有根树的DFS序
32 void DFS(int x){
33 dfn.pb(x);
34 for (/*x的所有儿子y*/){
35 DFS(y);
36 }
37 return;
38 }
39 //DFS(root);
40
41
42 //有根树的BFS序
43 void BFS(int root){
44 //将root加入队列q
45 while(!q.empty()){
46 //x = q队首元素;
47 //x出队;
48 for (/*x的所有儿子y*/){
49 //y入队;
50 }
51 }
52 return;
53 }
54
55 //BFS(root);
56 //q中出现的元素顺序即BFS序
链表实现:
1 const int N = 100001;
2 const int M = 100001;
3
4 struct Node{
5 int where;//记录Node的下标
6 Node *next;
7 } *head[N + 1], a[M];//a[M]与a[N]在有根树内等价,a[]用来存Node
8 //head[i] -> son -> sonson -> sonsonson...
9
10 int n, father[N + 1], l = 0;
11
12 void addEdge(int x, int y){
13 a[++l].where = y;//先++
14 a[l].next = head[x];
15 head[x] = &a[l];
16 father[y] = x;
17 return;
18 }
19
20 void PrintSon(int x){
21 for (Node *p = head[x]; p; p = p->next){
22 cout << p->where << " ";
23 }
24 return;
25 }
26
27 void DFS2(int from, int x){
28 for (/*x的所有相邻节点y*/){
29 if (y != from){
30 DFS2(x, y);
31 }
32 }
33 return;
34 }
35 //DFS(-1, x);
36
37 void BFS2(int x){
38 //将x加入队列q,x的来源节点为空
39 while(!q.empty()){
40 /*
41 x = q队首元素
42 from = x的来源节点
43 x出队
44 */
45 for (/*x的所有相邻节点y*/){
46 if (y != from){
47 //y入队
48 //y的来源节点为x
49 }
50 }
51 }
52 return;
53 }
54 //BFS(x);
无根树的求树上路径模板题:
1 inline int rd(){
2 int x=0,f=1;char ch=getchar();
3 while (ch<'0'||ch>'9') {if(ch=='-')f=-1; ch=getchar();}
4 while (ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0'; ch=getchar();}
5 return x*f;
6 }
7
8 const int N = 1000001;
9 VI edges[N + 1];
10 int pre[N + 1], l, c[N + 1];
11
12 inline void DFS(int x){
13 for (auto i : edges[x]){
14 if (i != pre[x]){
15 pre[i] = x;
16 DFS(i);
17 }
18 }
19 }
20
21 int main(){
22 ios;
23 int n, x, y, u, v;
24 n = rd();
25 for (int i = 1;i <= n - 1;i++){
26 x = rd();
27 y = rd();
28 edges[x].pb(y);
29 edges[y].pb(x);
30 }
31 u = rd();
32 v = rd();
33 pre[u] = -1;
34 DFS(u);
35 l = 0;
36 for (int i = v;i != u;i = pre[i]){
37 c[++l] = i;
38 }
39 c[++l] = u;
40 for (int i = l; i;--i){
41 cout << c[i] << " ";
42 }
43 return 0;
44 }
接下来我们说一说树的直径:
树的直径是指树上任意两个节点之间最长(经过边的数量)的路径。
一棵树可以存在很多条直径,其长度相等。
树的直径的中间节点被称为树的中心,如果直径上的节点总数是偶数,则有两个中心。
树的中心到其他点的最长路径最短。
树的直径可以用两次搜索求得,第一次从任意节点开始搜索,找出距离最远的节点a;
第二次从节点a出发,找到距离节点a最远的节点b,则路径a-b即为树的直径。
(反证法可证)
然后我们把时间交给树的重心
对于一颗无根树而言,当一个节点被选为根节点时,
它底下每个子节点的子树的大小(子树包含的节点数目)的最大值最小的那个节点,
被称为树的重心。
一棵树有可能有一个或两个重心。
树的重心有以下性质:
当重心为根节点时,它底下的每个子树的大小不大于整棵树大小的一半;
重心到其他所有节点的距离之和(路径长度)最小;
树的重心可以通过枚举根节点,然后递归求出所有子节点的子树大小,最后进行比较得到。