一、理解与感悟
1、树结点需要由父到子,有时也需要由子到父,所以结构体需要修改如下:
//树的结构体+存储数组
struct Node {
int id; // 当前结点ID
int father; // 爸爸
int left; // 左结点ID
int right; // 右结点ID
int depth; // 深度,不用再次dfs去探测
} t[N];
2、题目只说了表示从结点x到结点y(约定根结点为1)
,这样可不一定是从父到子,也可能是从子到父,需要两头看一下。
3、建图:如果出题人使坏,可能会给出 A->B,B->A的数据,需要我们记录是否节点已经录入完成,已完成的不能再次处理,防止死循环。
后来才知道,出题人没那么坏,他给出的数据确实是先是爹,后是儿子,没有例外,良心!
4、最大深度:这个好办,创建树时就已经逐个+1进行创建,可以遍历一次找出最大的深度即可。
5、最大宽度:这个也好办,因为树是现成的,我们只需要一个桶来记录每层的个数,找出最大个数就行了。
6、结点u到结点v间距离
这个是本题的关键所在!因为题目中说的很含糊,什么到根的边数,根本不是到根,是到它们两个结点的公共最近祖先
!
获取两个结点的公共最近祖先有现成的算法:
//最近公共祖先默认是根结点,就是1,英文简写:lowestCommonAncestor
int lca(int x, int y) {
st[x] = 1; //把x的节点记录已走过
while (t[x].father) { //遍历至根节点
x = t[x].father; //更新遍历爸爸
st[x] = 1; //记录已走过
}
//遍历至x节点已走过的节点,找到最近公共祖先
while (!st[y])y = t[y].father;
return y;
}
//按题意输出
cout << (t[u].depth - t[r].depth) * 2 + (t[v].depth - t[r].depth) << endl;
二、C++代码
#include <bits/stdc++.h>
using namespace std;
int n, x, y, u, v;
const int N = 110;
//树的结构体+存储数组
struct Node {
int id; // 当前结点ID
int father; // 爸爸
int left; // 左结点ID
int right; // 右结点ID
int depth; // 深度,不用再次dfs去探测
} t[N];
//记录边的关系
typedef pair<int, int> PII;
PII a[N];
//是不是使用过
int st[N];
//用来计某一层的结点个数
int bucket[N];
//通过递归构建二叉树
void dfs(int u) {
//u是老大,找出它的左儿子和右儿子
for (int i = 1; i < n; i++) {//遍历所有关系
if (a[i].first == u) {
//左结点为空,放入左结点
if (t[u].left == 0) t[u].left = a[i].second;
//否则放入右结点
else t[u].right = a[i].second;
//修改深度标识
t[a[i].second].depth = t[u].depth + 1;
t[a[i].second].father = u;
//递归创建子树
dfs(a[i].second);
}
}
}
//最近公共祖先默认是根结点,就是1,英文简写:lowestCommonAncestor
int lca(int x, int y) {
st[x] = 1; //把x的节点记录已走过
while (t[x].father) { //遍历至根节点
x = t[x].father; //更新遍历爸爸
st[x] = 1; //记录已走过
}
//遍历至x节点已走过的节点,找到最近公共祖先
while (!st[y])y = t[y].father;
return y;
}
int main() {
//二叉树结点个数
cin >> n;
//接下来的n-1行
for (int i = 1; i < n; i++) {
//构建二叉树
cin >> x >> y;
//记录关系
a[i] = {x, y};
}
//通过递归建树,构建二叉树
t[1].depth = 1, t[1].id = 1, t[1].father = 0, st[1] = 1;//根结点1
dfs(1);
//表示求从结点u到结点v的距离
cin >> u >> v;
//二叉树的深度
int maxDepth = 0;
for (int i = 1; i <= n; i++) maxDepth = max(maxDepth, t[i].depth);
cout << maxDepth << endl;
//二叉树的宽度
int maxWidth = 0;
for (int i = 1; i <= n; i++) bucket[t[i].depth]++;
for (int i = 1; i <= n; i++) maxWidth = max(maxWidth, bucket[i]);
cout << maxWidth << endl;
//结点u到结点v间距离:结点间距离的定义:由结点向根方向(上行方向)时的边数×2,与由根向叶结点方向(下行方向)时的边数之和。
//这里有一个最近公共祖先的问题,才能拿到正确答案
int r = lca(u, v);
//按题意输出
cout << (t[u].depth - t[r].depth) * 2 + (t[v].depth - t[r].depth) << endl;
return 0;
}
三、防止出题人使坏的代码
#include <bits/stdc++.h>
using namespace std;
int n, x, y, u, v;
const int N = 110;
//树的结构体+存储数组
struct Node {
int id; // 当前结点ID
int father; // 爸爸
int left; // 左结点ID
int right; // 右结点ID
int depth; // 深度,不用再次dfs去探测
} t[N];
//记录边的关系
typedef pair<int, int> PII;
PII a[N];
//是不是使用过
int st[N];
//用来计某一层的结点个数
int bucket[N];
//通过递归构建二叉树
void dfs(int u) {
//u是老大,找出它的左儿子和右儿子
for (int i = 1; i < n; i++) {//遍历所有关系
if (a[i].first == u && !st[a[i].second]) {
//标识已使用
st[a[i].second] = 1;
//左结点为空,放入左结点
if (t[u].left == 0) t[u].left = a[i].second;
//否则放入右结点
else t[u].right = a[i].second;
//修改深度标识
t[a[i].second].depth = t[u].depth + 1;
t[a[i].second].father = u;
//递归创建子树
dfs(a[i].second);
} else if (a[i].second == u && !st[a[i].first]) {
st[a[i].first] = 1;
if (t[u].left == 0) t[u].left = a[i].first;
else t[u].right = a[i].first;
t[a[i].first].depth = t[u].depth + 1;
t[a[i].first].father = u;
dfs(a[i].first);
}
}
}
//最近公共祖先默认是根结点,就是1,英文简写:lowestCommonAncestor
int lca(int x, int y) {
st[x] = 1; //把x的节点记录已走过
while (t[x].father) { //遍历至根节点
x = t[x].father; //更新遍历爸爸
st[x] = 1; //记录已走过
}
//遍历至x节点已走过的节点,找到最近公共祖先
while (!st[y])y = t[y].father;
return y;
}
int main() {
//二叉树结点个数
cin >> n;
//接下来的n-1行
for (int i = 1; i < n; i++) {
//构建二叉树
cin >> x >> y;
//记录关系
a[i] = {x, y};
}
//通过递归建树,构建二叉树
t[1].depth = 1, t[1].id = 1, t[1].father = 0, st[1] = 1;//根结点1
dfs(1);
//表示求从结点u到结点v的距离
cin >> u >> v;
//二叉树的深度
int maxDepth = 0;
for (int i = 1; i <= n; i++) maxDepth = max(maxDepth, t[i].depth);
cout << maxDepth << endl;
//二叉树的宽度
int maxWidth = 0;
for (int i = 1; i <= n; i++) bucket[t[i].depth]++;
for (int i = 1; i <= n; i++) maxWidth = max(maxWidth, bucket[i]);
cout << maxWidth << endl;
//结点u到结点v间距离:结点间距离的定义:由结点向根方向(上行方向)时的边数×2,与由根向叶结点方向(下行方向)时的边数之和。
//这里有一个最近公共祖先的问题,才能拿到正确答案
memset(st, 0, sizeof st);
int r = lca(u, v);
//按题意输出
cout << (t[u].depth - t[r].depth) * 2 + (t[v].depth - t[r].depth) << endl;
return 0;
}