一、实验内容
1.编写程序判断树是否同构?其中同构是指给定两棵树T1和T2。如果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
*/