根据所给的前序和中序序列,构建二叉树

目录

实验目的及内容

解题思路

需求分析:有三大需求

1.根据所给的前序和中序序列,构建二叉树

2.按要求格式,前序输出这棵二叉树

3.检查这棵二叉树,有没有根直接到叶子这样的路径,若有,则计算一下“目标整数值”

实验代码及注释

输入输出说明及结果截图

第一棵树(存在根到叶子的路径)

第二棵树(不存在这种路径) 

第三棵树(存在两条这样的路径) ​编辑 

心得体会


实验目的及内容

解题思路

需求分析:有三大需求

1.根据所给的前序和中序序列,构建二叉树

这部分最复杂,我是采用递归的方法来做的。

总体思路为:通过递归,不断压缩中序和先序序列的长度,从而确定左右子树。左右子树的根节点就是左右孩子。

首先,用两个数组preorder、inorder分别存储用户给出的前序序列和中序序列,然后,必须借助这两个数组来进行构造↓↓↓↓

如图所示

第一步.从前序序列中找根(对于每一棵树,它的根总是第一个在前序序列中出现);

第二步.从中序序列中确定左右子树(在一棵树的范围内,根节点左边就是左子树、右边就是右子树),

  • 同时左子树的根节点就是根节点root的左孩子,右子树的根节点就是根节点root的右孩子。
  • 一直递归。。。如此,便求出了每个节点的左右孩子

终止条件:对照根和中序序列,确定出的左/右子树只有一个节点时;

每一次递归,我们都需要确定左右子树的范围(包含哪些节点),所以,需要用四个整形变量记录前序序列和中序序列中左右子树的范围;

假设A在中序序列里面的位置为   rootIndex
 

关键点:找出先序序列中左右子树的分界点(范围):

方法:两种序列中,左子树和右子树的大小相等,从而可以求出分界点

可知,中序序列中,左子树的范围是:il到 il+rootIndex-1;右子树的范围是:rootIndex+1到        ir;

          前序序列中,左子树的范围是:pl+1到pl+1+leftsize;右子树范围是:pl+1+leftsize+1到

pr;

2.按要求格式,前序输出这棵二叉树

这部分主要是一些细节处理,有点繁杂,但是不难想,主要是多试几次。实际上就是在基本的前序输出基础上再加一个缩进的效果,其规律是:只要访问了左右孩子,就要多输出一个空格

3.检查这棵二叉树,有没有根直接到叶子这样的路径,若有,则计算一下“目标整数值”

这部分较为简单,由于是二叉树,所以只要查一下左右两棵子树是否是叶子即可,如果是叶子,那么就存在这样的路径

实验代码及注释

TIPS:为了优化时间复杂度,我额外开一个map数组,专门存放各个节点在中序序列里的位置下标,这样每次递归就不用扫描查找相应根结点的下标了(扫描查找时间复杂度为O(n));

借助map数组进行查找的时间复杂度为O(1),这样创建这课二叉树的时间复杂度为O(logn),但是由于需要输出和输入,所以整体代码的时间复杂度是O(n);

#include <iostream>
using namespace std;
struct TreeNode
{
	int val;
	TreeNode *left;
	TreeNode *right;
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}//C++类的初始化函数
};

// 二叉树创建函数(递归)
TreeNode *buildTree(int*map,int *preorder, int *inorder, int pl, int pr, int iL, int iR)
{
	//递归终止条件,如果算出来的左/右子树为空时,返回NULL
	if (pl > pr || iL > iR)
		{
			return NULL;
		}
    //总体的根为先序中的第一个元素
	int rootVal = preorder[pl];
	TreeNode *root = new TreeNode(rootVal);

	// 在中序序列中,借助map数组找到当前根节点的位置rootIndex
	int rootIndex = map[preorder[pl]];

	// 计算出左子树的长度,从而也得出了右子树的范围
	int leftSize = rootIndex - iL;

	//继续递归建立左右子树
	root->left = buildTree(map,preorder, inorder, pl + 1, pl + leftSize, iL, rootIndex - 1);
	root->right = buildTree(map,preorder, inorder, pl + leftSize + 1, pr, rootIndex + 1, iR);

	return root;//左子树的根节点就是父节点的左孩子(或右子树的根节点是父节点的右孩子),故返回根节点
}

// 按要求输出前序序列
void printPreorder(TreeNode *root, int depth)
{
	if (root == NULL)
		{
			return;
		}
	//用depth来标记是第几层孩子,每访问一次孩子,层数+1
	for (int i = 0; i < depth; i++)
		{
			cout << "  ";
		}
	cout << "|" << root->val << endl;
	printPreorder(root->left, depth + 1);
	printPreorder(root->right, depth + 1);
}


// 判断一个节点是否为叶子的函数
bool IsLeaf(TreeNode *tmp)
{
	if(tmp==NULL)
		{
			return false;
		}
	if (tmp->left == NULL && tmp->right == NULL)
		{
			return true;
		}
	else
		{
			return false;
		}
}
// 判断是否有根到叶子的节点函数,若有,则按要求计算目标整数值,并输出
void JudgeSumPrint(TreeNode *root)
{
	TreeNode *tmp;
	tmp = root;
	bool L = IsLeaf(tmp->left);
	bool R = IsLeaf(tmp->right);
	int sumL=root->val;
	int sumR=root->val;
	if (tmp == NULL)
		{
			return ;
			cout<<"根结点为空,不存在从根到叶子的路径"<<endl;
		}
	if(L&&R)
		{
			cout<<"存在两条根到叶子的路径"<<endl;
			cout<<"左边这条路径的值为:";
			sumL+=tmp->left->val;
			cout<<sumL<<endl;
			cout<<"右边这条路径的值为:";
			sumR+=tmp->right->val;
			cout<<sumR<<endl;

		}
	else if(L)
		{
			cout<<"存在一条根到叶子的路径"<<endl;
			cout<<"左边这条路径的值为:";
			sumL+=tmp->left->val;
			cout<<sumL<<endl;
		}
	else if(R)
		{
			cout<<"存在一条根到叶子的路径"<<endl;
			cout<<"右边这条路径的值为:";
			sumR+=tmp->right->val;
			cout<<sumR<<endl;
		}
	else
		{
			cout<<"不存在从根到叶子的路径"<<endl;
		}
}

//************//
int main()
{
	int n; // 节点个数
	cout << "请输入节点个数:" << endl;
	cin >> n;
	int *preorder = new int[n];
	int *inorder = new int[n];
	int *map=new int[n];//用来记录各个节点在中序序列里的位置
	cout << "请输入二叉树的前序序列:" << endl;
	for (int i = 0; i < n; i++)
		{
			cin >> preorder[i];
		}
	cout << "请输入二叉树的中序序列:" << endl;
	for (int i = 0; i < n; i++)
		{
			cin >> inorder[i];
			map[inorder[i]]=i;
		}

	TreeNode *root = buildTree(map,preorder, inorder, 0, n - 1, 0, n - 1);
	cout << "前序输出这棵二叉树:" << endl;
	printPreorder(root, 0);
	JudgeSumPrint(root);
	delete[]map;
	delete[] preorder;
	delete[] inorder;//new完一定要delete!
	return 0;
}

结果截图

第一棵树(存在根到叶子的路径)

前序(VLR):1 2 3 4 5 6

中序(LVR): 3 2 5 4 1 6

第二棵树(不存在这种路径) 

前序(VLR):1 2 3 4 5 6

中序(LVR): 3 2 4 1 6 5

 

第三棵树(存在两条这样的路径)  

心得体会

递归是一个树结构,每个分支都探究到最远,发现无法继续的时候才往回走,每个节点只会访问一次,因此,终止条件的判定非常重要

同理,树的许多操作也适合用递归来写,虽然时间复杂度上没有多少优化,空间复杂度也不友好,但是其代码却很简洁好看,也许,这就是递归算法的优美之处吧~~

  • 3
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值