华东理工数据结构实验-树

一、实验内容

1.编写程序判断树是否同构?其中同构是指给定两棵树T1T2。如果T1可以通过若干次左右孩子互换就变成T2,则称两棵树是同构的,输出True,否则输出False

输入:第一行N,表示树的结点数;

第二行开始:结点数据   左孩子编号   右孩子编号,如果无孩子结点记为“-1”

输入样例:

8

A 1 2

B 3 4

C 5 -1

D -1 -1

E 6 -1

G 7 -1

F -1 -1

H -1 -1

8

G -1 4

B 7 6

F -1 -1

A 5 1

H -1 -1

C 0 -1

D -1 -1

E 2 -1

输出样例:True

2、给定一棵二叉搜索树,请 按中序遍历将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。例如,将左下图的二叉搜索树转换为右下图的树。

输入样例:5 3 6 2 4 null 8 1 null null null 7 9

输出样例:1 null 2 null 3 null 4 null 5 null 6 null 7 null 8 null 9

3.给定一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

输入样例1:

输入:2 1 3

输出:True



输入样例2:

输入:5 1 4 null null 3 6

输出:False

4. 给定一个二叉树, 编写算法计算二叉树中任意两个结点的公共祖先。其中,输入第一行为二叉树序列,第二行和第三行分别为两个节点编号;输出:两个节点的公共祖先。例如:

输入样例1:

输入:

3 5 1 6 2 0 8 null null 7 4

5

1

输出: 3



输入样例2:

输入:

3 5 1 6 2 0 8 null null 7 4

5

4

输出: 5

 二.实现代码

1.实验一使用了结构体来表示两棵树,通过递归来判断三种情况,1. 判断根结点是否为空,若两个根结点都为空则是同构树;如果一个为空一个不为空则不是同构树2. 两个根结点都存在,但是对应的数值不同,不是同构树3. 判断两个根结点的左右子树是否相同或者是否是顺序调换。

参考链接:PTA 7-3 树的同构 (25分)_涵小呆的博客-CSDN博客

#include<bits/stdc++.h>
using namespace std;
#define NULL -1
struct Node
{
	char data;
	int lchild;
	int rchild;
}tree1[15], tree2[15];
 
//建树并返回树的根结点
int BuildTree(struct Node tree[])
{
	int n;
	int label[15];
	char data1;
	int left,right;
	memset(label, 0, sizeof(label));
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin>>data1>>left>>right;
		tree[i].data = data1;
		if (left!= -1)
		{
			tree[i].lchild = left;
			label[left] = 1;
		}
		else
			tree[i].lchild = NULL;
		if (right!=-1)
		{
			tree[i].rchild = right;
			label[right] = 1;
		}
		else
			tree[i].rchild = NULL;
	}
	int root = NULL;//初始化根结点
 
	for (int i = 0; i < n; i++)
	{
		if (!label[i]) root = i;
	}
	return root;
}
bool judge(int root1, int root2)
{
	if (root1 == NULL && root2 == NULL) {//判断根节点是否都为空
		return true;//都为空则相同
	}
	if (root1 == NULL && root2 != NULL || root1 != NULL && root2 == NULL) {
		return false;//一个根节点为空另外一个不为空则为假
	}
	if (tree1[root1].data != tree2[root2].data) {
		return false;//两棵树根节点都存在,但数值不同则为假
	}
	if (tree1[tree1[root1].lchild].data == tree2[tree2[root2].lchild].data) {
		//如果左子树的值相等则判断右子树
		return judge(tree1[root1].rchild, tree2[root2].rchild);
	}
	else {
		//否则判断是否第二棵树是在第一课树左右子树调换之后得到的
		return judge(tree1[root1].lchild, tree2[root2].rchild)
			&& judge(tree1[root1].rchild, tree2[root2].lchild);
	}
}
 
int main()
{
	int root1 = BuildTree(tree1);
	int root2 = BuildTree(tree2);
	if (judge(root1, root2))
		cout << "True\n";
	else
		cout << "False\n";
	return 0;
 
}
 

2.实验二使用了数组和结构体,先使用层序遍历构造二叉树,将结点层序序列已经存进vector中,再按中序遍历顺序把节点放入node中,最后创建树每个节点的左子树为空,右子树指向中序遍历的下一个节点。

参考代码:C++根据层序遍历序列构造二叉树,再层序遍历二叉树_loreal8的博客-CSDN博客_根据层序遍历构造二叉树

#include<bits/stdc++.h>
using namespace std;
#define MaxSize 1000
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
};
typedef struct TreeNode treeNode;
int node[101];
//treeNode* node[101]; 或者可以使用数组来存放中序遍历的结点而不是单单结点的值 
//按照层序遍历构造二叉树
//假设层序序列已经存进vector中,并且是一颗完全二叉树
treeNode* createBinTree(vector<int> arr)//需要用队列来实现 
{
	queue<treeNode*> q;//每个队列都存着树的每个结点
	 //如果层序序列为空,返回空树
    if (arr.empty())
    {
    	return nullptr;
    }
    //否则先创建一个新结点,并用指针去指向它,最后返回的也是头结点 
    treeNode* head=new treeNode;
    head->val=arr[0];//存放数组首元素
    head->left=nullptr;
    head->right=nullptr;
	q.push(head);//新结点入队
	
	treeNode* T;//建一个临时量
	int cnt=1;//将arr中结点的值后移
	while(!q.empty())
	{
		T=q.front();//给临时变量队列的头结点,给他分配左右孩子
		q.pop();//安排过了,出队
		
		/*先安排左孩子*/
		if(cnt<arr.size()&&arr[cnt]!=-1)//cnt只要不超过数组的有效长度,就有左孩子(空结点当-1)
		{
			T->left=new treeNode;
			T->left->val=arr[cnt];
			T->left->left=nullptr;
			T->left->right=nullptr;
			cnt++;//后移 
			q.push(T->left);//左孩子入队 
		}
		else {
			T->left=nullptr;
			cnt++;
		}
		/*再安排右孩子*/
		 if(cnt<arr.size()&&arr[cnt]!=-1)//cnt只要不超过数组的有效长度,就有左孩子(空结点当-1)
		{
			T->right=new treeNode;
			T->right->val=arr[cnt];
			T->right->left=nullptr;
			T->right->right=nullptr;
			cnt++;//后移 
			q.push(T->right);
		}
		else {
			T->right=nullptr;
			cnt++;
		}
	}
	return head; 
}
int size=0;
void inorder(struct TreeNode* root)//按中序遍历顺序把节点放入node中
{
    if(root)
    {
        inorder(root->left);
        node[size++]=root->val;
        inorder(root->right);
    }
}
/*
void inorder(struct TreeNode arr[],struct TreeNode* root)//按中序遍历顺序把节点放入node中
{
    if(root)
    {
        inorder(root->left);
        arr[size++]=root;
        inorder(root->right);
    }
}
void increasingBST(struct TreeNode* root)//创建树,因为该树的结构固定,所以把结点都放进node[]里面 
{
    size=0;
    inorder(node,root);
    for(int i=0;i<size-1;i++)//每个节点的左子树为空,右子树指向中序遍历的下一个节点
    {
        node[i]->left->val=-1;//nullptr使用-1来替代 
        node[i]->right=node[i+1];
    }
    node[size-1]->left=node[size-1]->right=nullptr;//最尾节点特殊处理,左右子树都指向空
}
*/
int main()
{
	cout<<"输入后请按回车,再按ctrl+z中断输入!输入和输出中null使用-1代替"<<endl; 
	vector<int> a;
	int tmp;
	while(cin>>tmp)
	{
		a.push_back(tmp);
	}
	treeNode* root=createBinTree(a);
	inorder(root);
	//increasingBST(root);
	
	for(int i=0;i<size;i++)
	{
		cout<<node[i]<<" "<<-1<<" ";
	}
	return 0;
}
//输入样例:5 3 6 2 4 -1 8 1 -1 -1 -1 7 9

3.实验三使用了数组和结构体,直接中序遍历,二叉搜索树的中序遍历是一个递增序列,所以我们只需要把这个中序遍历保存下来,然后判断这是个递增序列即可。

#include<bits/stdc++.h>
using namespace std;
#define MaxSize 1000
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
};
typedef struct TreeNode treeNode;
int node[101];
//treeNode* node[101]; 或者可以使用数组来存放中序遍历的结点而不是单单结点的值 
//按照层序遍历构造二叉树
//假设层序序列已经存进vector中,并且是一颗完全二叉树
treeNode* createBinTree(vector<int> arr)//需要用队列来实现 
{
	queue<treeNode*> q;//每个队列都存着树的每个结点
	 //如果层序序列为空,返回空树
    if (arr.empty())
    {
    	return nullptr;
    }
    //否则先创建一个新结点,并用指针去指向它,最后返回的也是头结点 
    treeNode* head=new treeNode;
    head->val=arr[0];//存放数组首元素
    head->left=nullptr;
    head->right=nullptr;
	q.push(head);//新结点入队
	
	treeNode* T;//建一个临时量
	int cnt=1;//将arr中结点的值后移
	while(!q.empty())
	{
		T=q.front();//给临时变量队列的头结点,给他分配左右孩子
		q.pop();//安排过了,出队
		
		/*先安排左孩子*/
		if(cnt<arr.size()&&arr[cnt]!=-1)//cnt只要不超过数组的有效长度,就有左孩子(空结点当-1)
		{
			T->left=new treeNode;
			T->left->val=arr[cnt];
			T->left->left=nullptr;
			T->left->right=nullptr;
			cnt++;//后移 
			q.push(T->left);//左孩子入队 
		}
		else {
			T->left=nullptr;
			cnt++;
		}
		/*再安排右孩子*/
		 if(cnt<arr.size()&&arr[cnt]!=-1)//cnt只要不超过数组的有效长度,就有左孩子(空结点当-1)
		{
			T->right=new treeNode;
			T->right->val=arr[cnt];
			T->right->left=nullptr;
			T->right->right=nullptr;
			cnt++;//后移 
			q.push(T->right);
		}
		else {
			T->right=nullptr;
			cnt++;
		}
	}
	return head; 
}
int size=0;
void inorder(struct TreeNode* root)//按中序遍历顺序把节点放入node中
{
    if(root)
    {
        inorder(root->left);
        node[size++]=root->val;
        inorder(root->right);
    }
}
bool isBST()
{
	for(int i=1;i<size;i++)//判断是否为二叉搜索树就判断中序遍历结果是否为递增即可
	{
		if(node[i-1]>node[i])
		return 0; 
	}
	return 1; 
}
int main()
{
	cout<<"输入后请按回车,再按ctrl+z中断输入!输入和输出中null使用-1代替"<<endl; 
	vector<int> a;
	int tmp;
	while(cin>>tmp)
	{
		a.push_back(tmp);
	}
	treeNode* root=createBinTree(a);
	inorder(root);
	if(isBST()) 
		cout<<"True";
	else
	    cout<<"False";
	return 0;
}

4.实验四使用了数组和结构体,首先根节点肯定是二者的父节点,题目是去找最近的父节点,那么有以下几种情况:两个节点都在根节点的左子树中,递归的去左子树中去找最近的父节点、两个节点都在根节点的右子树中,递归的去右子树中去找最近的父节点、两个节点分别在左右子树中,这个时候它俩的最近的父节点就是根节点。

参考学习:算法-二叉树中俩节点的最近公共祖先_uncle_ll的博客-CSDN博客_两个节点的最近公共祖先

#include<bits/stdc++.h>
using namespace std;
#define MaxSize 1000
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
};
typedef struct TreeNode treeNode;
int node[101];
//treeNode* node[101]; 或者可以使用数组来存放中序遍历的结点而不是单单结点的值 
//按照层序遍历构造二叉树
//假设层序序列已经存进vector中,并且是一颗完全二叉树
treeNode* createBinTree(vector<int> arr)//需要用队列来实现 
{
	queue<treeNode*> q;//每个队列都存着树的每个结点
	 //如果层序序列为空,返回空树
    if (arr.empty())
    {
    	return nullptr;
    }
    //否则先创建一个新结点,并用指针去指向它,最后返回的也是头结点 
    treeNode* head=new treeNode;
    head->val=arr[0];//存放数组首元素
    head->left=nullptr;
    head->right=nullptr;
	q.push(head);//新结点入队
	
	treeNode* T;//建一个临时量
	int cnt=1;//将arr中结点的值后移
	while(!q.empty())
	{
		T=q.front();//给临时变量队列的头结点,给他分配左右孩子
		q.pop();//安排过了,出队
		
		/*先安排左孩子*/
		if(cnt<arr.size()&&arr[cnt]!=-1)//cnt只要不超过数组的有效长度,就有左孩子(空结点当-1)
		{
			T->left=new treeNode;
			T->left->val=arr[cnt];
			T->left->left=nullptr;
			T->left->right=nullptr;
			cnt++;//后移 
			q.push(T->left);//左孩子入队 
		}
		else {
			T->left=nullptr;
			cnt++;
		}
		/*再安排右孩子*/
		 if(cnt<arr.size()&&arr[cnt]!=-1)//cnt只要不超过数组的有效长度,就有左孩子(空结点当-1)
		{
			T->right=new treeNode;
			T->right->val=arr[cnt];
			T->right->left=nullptr;
			T->right->right=nullptr;
			cnt++;//后移 
			q.push(T->right);
		}
		else {
			T->right=nullptr;
			cnt++;
		}
	}
	return head; 
}
treeNode* lowestCommonAncestor(treeNode* root,int p,int q)
{
	if(root==nullptr||root->val==p||root->val==q) return root;
	treeNode* Left=lowestCommonAncestor(root->left,p,q);
	treeNode* Right=lowestCommonAncestor(root->right,p,q);
	if(Left&&Right) return root;
	if(Left) return Left;
	if(Right) return Right;
}
int main()
{
	cout<<"输入后请按回车,再按ctrl+z中断输入!输入和输出中null使用-1代替"<<endl; 
	int p,q;
	vector<int> a;
	int tmp;
	while(cin>>tmp)
	{
		a.push_back(tmp);
	}
	int k=a.size();//因为输入需要手动中断的特殊性,直接将p,q放在a数组的最后两个 
	p=a[k-1];q=a[k-2];
	a.pop_back();
	a.pop_back();
	treeNode* root=createBinTree(a);
	treeNode* res=lowestCommonAncestor(root,p,q);
	cout<<res->val;
	return 0;
}
/*输入样例1:
输入:
3 5 1 6 2 0 8 -1 -1 7 4
5
1
输出: 3

输入样例2:
输入:
3 5 1 6 2 0 8 -1 -1 7 4
5
4
输出: 5
*/

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

7_lining

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值