目录
一、题目原文
给定一个 n 个结点(编号 1∼n)构成的二叉树,其根结点为 1 号结点。
进行 m 次询问,每次询问两个结点之间的最短路径长度。
树中所有边长均为 1 。
输入格式
第一行包含一个整数 T,表示共有 T 组测试数据。
每组数据第一行包含两个整数 n,m 。
接下来 n 行,每行包含两个整数,其中第 i 行的整数表示结点 i 的子结点编号。如果没有子结点则输出 −1 。
接下来 m 行,每行包含两个整数,表示要询问的两个结点的编号。
输出格式
每组测试数据输出 m 行,代表查询的两个结点之间的最短路径长度。
数据范围
1 ≤ T ≤ 10,
1 ≤ n , m ≤ 1000
输入样例:
1
8 4
2 3
4 5
6 -1
-1 -1
-1 7
-1 -1
8 -1
-1 -1
1 6
4 6
4 5
8 1
输出样例:
2
4
2
4
二、题目分析
核心思想:两点间距离->两点到他们最近的公共点距离之和(利用结点到根节点的距离来求出)
实际上,两点间距离有一种特殊情况(正如下图中的8号节点和2号节点间的距离),2号节点既是两点中的一个,也是2号与8号最近的公共点,但这种情况也不违背以上求解思想,此处解释仅为打消读者顾虑。
三、代码思想
Step1:造树(使用动态数组指示动态分配空间)
Step2:找结点到根的距离(使用动态数组记录路径)
Step3:求两结点距离(通过“游标”找公共点)
四、代码实现
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
struct Treenode{ //树节点结构体定义
int data; //数据域
Treenode* lchild; //左孩子指针
Treenode* rchild; //右孩子指针
Treenode* parent; //指向其双亲节点的指针
};
int main(){
int t;
scanf("%d",&t); //题目要求最多输入T组数据
for(int i=0;i<t;i++){
int n,m;
scanf("%d%d",&n,&m); //题目要求每组数据n个节点,m组求距离的节点
vector<Treenode*> vec1(n+1); //用动态数组储存指针,每个指针指向与数组下标相同的节点,为与树节点的编号形成一一对应关系,0号下标不使用
for(int j=1;j<=n;j++){
vec1[j]=new Treenode; //使用指针指向动态申请空间
vec1[j]->data=j; //数据域置为数字1~n
}
vec1[1]->parent=NULL; //根节点的双亲结点指向空
for(int j=1;j<=n;j++){
int left,right;
scanf("%d%d",&left,&right); //循环充入各节点的左右孩子信息
if(left!=-1){ //若左孩子不为空
vec1[j]->lchild=vec1[left]; //链接左孩子
vec1[left]->parent=vec1[j]; //左孩子的双亲节点改为当前节点
}else{
vec1[j]->lchild=NULL; //左孩子为空则将指针指向空
}
if(right!=-1){ //右孩子同上
vec1[j]->rchild=vec1[right];
vec1[right]->parent=vec1[j];
}else{
vec1[j]->rchild=NULL;
}
}
int leftnode,rightnode; //要求距离的两结点编号(这里为方便描述,将其分为“左”和“右”两个点,仅为一名,并不与实际上的空间关系完全相符)
for(int k=0;k<m;k++){
scanf("%d%d",&leftnode,&rightnode); //循环充入m对求距离的节点
vector<int> leftrecord; //使用动态数组记录左节点找根节点的路径
Treenode* p=vec1[leftnode]; //新建一个p指针,指向左节点
while(p!=NULL){ //若左节点非空
leftrecord.push_back(p->data); //将左节点数据域压入动态数组
p=p->parent; //p指针转而指向左节点的双亲节点
}
vector<int> rightrecord; //右节点同理
p=vec1[rightnode];
while(p!=NULL){
rightrecord.push_back(p->data);
p=p->parent;
}
int l=leftrecord.size()-1; //设置变量l,指示上一步的leftrecord动态数组中最后一个元素的下标,亦即为解析中的“左游标”
int r=rightrecord.size()-1; //设置变量r,指示上一步的rightrecord动态数组中最后一个元素的下标,亦即为解析中的“右游标”
while(1){
if(leftnode<0||rightnode<0||leftrecord[l]!=rightrecord[r]){ //当游标越界或者找到了最近的公共点的上一位不同元素的结点时,退出循环(详情看上一标题之Step.3)
break;
}
l--; //若还未满足以上条件,则左游标和右游标不断在动态数组中往前移动
r--;
}
printf("%d\n",l+r+2); //打印最终所得出的距离
}
}
return 0;
}