BUAA(2021春) 快逃离卷怪统治的BUAA——求树中的最短路径+中序和后序恢复二叉树(已上传题解)

48 篇文章 152 订阅

看前须知

要点介绍和简要声明.

考试回顾

格式控制输入输出——期中考试模拟题(简单的分类讨论).

标识符的识别(期中考试题)——题目说的不清不楚但其实不难.

空闲空间申请模拟(期中考试题)——注意读题,难度其实一般.

网络打印机选择(北京学院数据结构18级期末压轴题)——伪树状数组(这题杀我)(ಥ_ಥ).

查家谱(士谔书院16级期末)——找最近公共祖先.

图的直径(士谔书院15级期末)——求有向图的最大路径:Floyd的妙用.

快逃离卷怪统治的BUAA——求树中的最短路径+中序和后序恢复二叉树.

题目内容

原题链接(测试地点).

问题描述

zhouEnshen一觉醒来,发现自己在一个卷怪统治的BUAA中上课,他多方打听,才知道如果要回到表世界,就必须穿越一个个教学楼,最终才能从这个恐怖的BUAA逃离
在这里插入图片描述

zhouEnshen发现这些教学楼的结构很像二叉树,同时他也买到了一份包含着每个教学楼的BUAA地图,他身处这个树形BUAA的根部,必须走到叶子节点处才能返回表世界。但zhouEnshen没有上帝视角,很难决定自己走哪条路,请你根据zhouEnshen已有的信息判断,他选择那条路才能最快逃脱(认为经过教学楼走廊数最少者可以最快逃脱)。为了简化问题,不考虑从一个教学楼到另一个之间的教学楼。

输入形式

多组输入数据,每组包含两行。分别为二叉树的中序遍历和后序遍历,每个节点代表一个教学楼,数字是该教学楼内的走廊的数目。节点之间用一个空格隔开。

在每组遍历中,不会出现空树或者重复数字的情况。保证数字在int范围内。

输出形式

对于每组数据,输出一行,为zhouEnshen可以选择的最快路径的终点(输出该叶节点的值即可),如果有两条以上最短路,输出值较小的那个终点。

样例

【输入】

3 2 1 4 5 7 6
3 1 2 5 6 7 4
10
10

【输出】

1
10

样例说明

【样例说明】

对于第一组数据恢复二叉树后,选择的路径为4 —> 2 —> 1
对于第二组数据恢复二叉树后,选择的路径为 10

题解

思考和详解

这个题有三个难点,第一个是怎么输入数据。没错,真的这是一个让人觉得不可思议的难点,想想看,如何测试多组数据而这些数据是由不知道长度的int型数组组成。我们之前在进行多组数据测试的时候都是利用数组的长度或则数据的组数来确定EOF的,但是这里不行,我们必须要先用字符串存下来实现NULL判断数据是否停止输入,再通过字符串转int型数组实现真正的数据读入,利用sscanf来读入,我相信绝大多数人都忘了怎么用

第二个难点是如何利用二叉树的中序遍历和后序遍历恢复二叉树,其实我们模拟在纸上的操作就行,首先后序数组的最后一个是根,然后在中序遍历数组里找到根作为分界点,分左右两边为左子树的中序遍历和右子树的中序遍历,同时后序遍历数组也利用中序数组的分界点确定左子树后序遍历的节点数和右子树后序遍历的节点数,然后递归重复处理操作知道数组用完为止。

第三个难点在于怎么求最短路径,其实利用**DFS(二叉树的前序遍历)**递归处理就OK。

参考代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    int path;
 }Tree,*Treep;
struct leafNode{		//树叶节点信息 
	int path;			//路径值 
	int val;			//该树叶节点的名 
}node[200];
Treep root=NULL,t;
int top=0;
struct TreeNode* buildTree(int* inorder, int inorderSize, int* postorder, int postorderSize){	//恢复二叉树 
    int p=postorderSize-1,i=0;	//p是后序遍历的倒数第一个数 
    struct TreeNode* newNode;
    if(postorder == NULL || inorder == NULL) return NULL;
    if(postorderSize <= 0 || inorderSize <= 0 )  return NULL;
    newNode=(struct TreeNode*)malloc(sizeof(struct TreeNode));
    newNode->val=postorder[p];	//利用后序遍历的倒数第一个构造根节点 
    newNode->left=newNode->right=NULL;
    for(i=0;i<inorderSize;i++)
        if(newNode->val == inorder[i])	//在中序遍历找到根节点 
        {
            newNode->left=buildTree(inorder,i,postorder,i);	//递归处理 
            newNode->right=buildTree(inorder+i+1,inorderSize-i-1,postorder+i,postorderSize-i-1);	//递归处理 
        }
    return newNode;
}
void CauculatePath(Treep t,int path)
{
    if(t!=NULL){
        t->path=path+t->val;	//算路径总值 
        CauculatePath(t->left,t->path);	//算路径总值 
        CauculatePath(t->right,t->path);	//算路径总值 
    }
}
void SortLeaves(Treep t)
{
    if(t!=NULL){
        if(t->left==NULL && t->right==NULL)
		{
			node[top].path=t->path,node[top].val=t->val;//	记录叶节点信息 
			top++;	//用数组存下来 
		}
        SortLeaves(t->left);
        SortLeaves(t->right);
    }
}
int compare(const void*p1,const void*p2)
{
	struct leafNode *a=(struct leafNode*)p1;
	struct leafNode *b=(struct leafNode*)p2;
	if(a->path!=b->path	)return a->path-b->path;	//根据路径总值排序 
	else return a->val-b->val;
}
int main()
{
	int inorder[200],postorder[200],inorderSize,postorderSize,tmp,i,path=0,a,offset;
	char s1[200],s2[200];
	char ch;
	while(gets(s1)!=NULL){		//难点 
  		gets(s2);
  		memset(inorder,0,sizeof(inorder));
  		memset(postorder,0,sizeof(postorder));
  		memset(node,0,sizeof(node));
  		root=NULL;
  		offset=top=0;
  		int readlen;
  		while(sscanf(s1+offset,"%d%n",&a,&readlen)==1)	//获得一个数 
		{
   			offset+=readlen;
   			inorder[top]=a;
   			top++;
  		}
 		offset=0,top=0;
  		while(sscanf(s2+offset,"%d%n",&a,&readlen)==1){	//获得一个数 
   			offset+=readlen;
  			postorder[top]=a;
   			top++;
  		}
		inorderSize=postorderSize=top;	//记录遍历数组的总数 
		top=0;
		root=buildTree(inorder,  inorderSize,  postorder,  postorderSize);//恢复二叉树 
		CauculatePath(root,path);	//计算路径总值 
		SortLeaves(root);			//存树叶结点 
		qsort(node,top,sizeof(struct leafNode),compare);	//排序 
		printf("%d\n",node[0].val);
	}
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值