下面我们来介绍一般意义上的树,

包括有根树无根树


有根树基本操作:

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即为树的直径。

(反证法可证)

 


 

然后我们把时间交给树的重心

对于一颗无根树而言,当一个节点被选为根节点时,

它底下每个子节点的子树的大小(子树包含的节点数目)的最大值最小的那个节点,

被称为树的重心。

一棵树有可能有一个或两个重心。

 

树的重心有以下性质

  当重心为根节点时,它底下的每个子树的大小不大于整棵树大小的一半;

  重心到其他所有节点的距离之和(路径长度)最小;

树的重心可以通过枚举根节点,然后递归求出所有子节点的子树大小,最后进行比较得到。

 

posted @ 2022-05-17 22:01  Conqueror712  阅读(97)  评论(0)    收藏  举报