浅析以动态数组构建二叉树并求其任意两结点间的距离——以北京邮电大学考研机试题为例

文章介绍了如何在给定的二叉树中,通过构造树结构并使用动态数组记录节点到根节点的距离,计算任意两点之间的最短路径长度。核心思想是找到两点到它们最近公共点的距离之和。
摘要由CSDN通过智能技术生成

目录

一、题目原文

二、题目分析

三、代码思想

Step1:造树(使用动态数组指示动态分配空间)

Step2:找结点到根的距离(使用动态数组记录路径)

Step3:求两结点距离(通过“游标”找公共点)

四、代码实现


一、题目原文

给定一个 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;
}

五、OJ测试

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值