树是由一个集合以及在该集合上定义的一种关系构成的,集合中的元素称为树的结点,所定义的关系称为父子关系。父子关系在树的结点之间建立了一个层次结构,在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点。
树的一些名词
节点的度:一个节点含有的子树的个数称为该节点的度;
叶节点或终端节点:度为0的节点称为叶节点;
非终端节点或分支节点:度不为0的节点;
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
兄弟节点:具有相同父节点的节点互称为兄弟节点;
树的度:一棵树中,最大的节点的度称为树的度;
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次;
堂兄弟节点:双亲在同一层的节点互为堂兄弟;
节点的祖先:从根到该节点所经分支上的所有节点;
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
森林:由m(m >= 0)棵互不相交的树组成的集合称为森林;
树的几种存储
1.双亲表示法
2.孩子表示法
3.邻接表
树的一些操作
1.树的节点计数(入门OJ1570)
这个很简单,直接做就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
struct node {
int v, next;
} e[50010 * 2];
int p[50010], k;
int cnt[50010];
int vis[50010];
int n;
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
void init() {
memset(p, -1, sizeof(p));
}
void insert(int u,int v) {
e[k].v = v;
e[k].next = p[u];
p[u] = k++;
}
void dfs(int u) {
vis[u] = 1;
for(int i = p[u]; i + 1; i = e[i].next) {
if(!vis[e[i].v]) {
dfs(e[i].v);
cnt[u] += cnt[e[i].v];
}
}
}
int main() {
init();
cin >> n;
for(int i = 1; i <= n; i++) {
cnt[i] = 1;
}
int u, v;
for(int i = 1; i < n; i++) {
read(u);read(v);
insert(u, v);
insert(v, u);
}
dfs(1);
for(int i = 1; i <= n; i++) {
printf("%d\n", cnt[i] - 1);
}
return 0;
}
2.树的深度计数(入门OJ5962)
这题也很简单,dfs跑一跑就好了
代码:
#include <bits/stdc++.h>
using namespace std;
int k, n, s;
int pre[100001], now[100001], son[100001], ans[100001];
bool vis[50001];
void insert(int a, int b) {
pre[++k] = now[a];
now[a] = k;
son[k] = b;
}
void dfs(int u, int r) {
vis[u] = 1;
ans[u] = r + 1;
int ans = 0, m = 0;
for(int i = now[u]; i; i = pre[i]) {
int k = son[i];
if(!vis[k])
dfs(k, r + 1);
}
}
int main() {
int a, b;
scanf("%d", &n);
for(int i = 1; i <= n - 1; i++) {
scanf("%d%d", &a, &b);
insert(a, b);
insert(b, a);
}
dfs(1, -1);
for(int i = 1; i <= n; i++)
printf("%d\n", ans[i]);
return 0;
}
3.树的重心(入门OJ1596)
树的重心也叫树的质心。简单来说就是,你删掉某个点后剩下的最大的树最小,这个点就是重心。
代码:
//一个重心
#include<iostream>
using namespace std;
struct node {
int v, nxt;
} e[100010];
int n, mi;
int p[50010], num;
int dp[50010];
bool vis[50010];
void insert(int u, int v) {
e[++num].v = v;
e[num].nxt = p[u];
p[u] = num;
}
void dfs(int u, int f) {
dp[u] = 1;
int mx = 0;
for (int i = p[u]; i; i = e[i].nxt) {
if (e[i].v != f) {
dfs(e[i].v, u);
dp[u] += dp[e[i].v];
mx = max(mx, dp[e[i].v]);
}
}
mx = max(mx, n - dp[u]);
if (mx < mi) {
mi = mx;
}
}
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
insert(x, y);
insert(y, x);
}
mi = 1e9;
dfs(1, 0);
cout << mi << endl;
}
//所有重心
#include<iostream>
using namespace std;
struct node {
int v, nxt;
} e[100010];
int n, mi;
int p[50010], num;
int dp[50010];
int mx[50010];
bool vis[50010];
void insert(int u, int v) {
e[++num].v = v;
e[num].nxt = p[u];
p[u] = num;
}
void dfs(int u, int f) {
dp[u] = 1;
for (int i = p[u]; i; i = e[i].nxt) {
if (e[i].v != f) {
dfs(e[i].v, u);
dp[u] += dp[e[i].v];
mx[u] = max(mx[u], dp[e[i].v]);
}
}
mx[u] = max(mx[u], n - dp[u]);
mi = min(mi, mx[u]);
}
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
insert(x, y);
insert(y, x);
}
mi = 1e9;
dfs(1, 0);
for (int i = 1; i <= n; i++) {
if (mx[i] == mi) {
cout << i << endl;
}
}
}
友情提示:注意输出格式
4.树的直径(入门OJ1584)
直径嘛,顾名思义,就是在树上找到两个点满足这两点间的距离最远(距离定义为连接两点的路径边权之和),这个距离就叫做树的直径。
代码:
#include<bits/stdc++.h>
using namespace std;
int n, m, cnt, maxx;
int to[100010], nxt[100010], val[100010];
int head[50010], s[50010];
char str[5];
void insert(int a, int b, int c) {
to[cnt] = b;
val[cnt] = c;
nxt[cnt] = head[a];
head[a] = cnt++;
}
void dfs(int x, int fa) {
maxx = (s[maxx] < s[x]) ? x : maxx;
for(int i = head[x]; ~i; i = nxt[i]) {
if(to[i] != fa) {
s[to[i]] = s[x] + val[i];
dfs(to[i], x);
}
}
}
int main() {
scanf("%d%d", &n, &m);
int a, b, c;
memset(head, -1, sizeof(head));
for(int i = 1; i <= m; i++){
scanf("%d%d%d%s", &a, &b, &c, &str);
insert(a, b, c);
insert(b, a, c);
}
dfs(1, 0);
s[maxx] = 0;
dfs(maxx, 0);
printf("%d", s[maxx]);
return 0;
}
差不多就是这样