PTA 树的同构 思路分析及代码解析 v1.0
一、前导
1. 需要掌握的知识
- 二叉树
- 递归函数的应用
2. 题目信息
- 题目来源:PTA / 拼题A
- 题目地址:树的同构
二、解题思路分析
1. 题意理解
- 输入数据
N //树1的结点数:随后N行,从0到N-1编号,第i行对应编号第i个结点
A 1 - //结点值、左子树结点编号、右子树结点编号(-表示空树)
B 3 4
...
2 //树2的结点数
B - 1 //树2的结点信息
A 4 5
...
- 输出数据
两棵树若同构输出Yes,否则输出No - 题意
判定两棵树是否‘同构’。题目定义了’同构’概念:树1若通过左右子树互换就可以变成树2(互换次数不限),则称两棵树“同构”。
2. 思路分析(重点)
先建树然后再比较
三、具体实现
1. 弯路和bug
- 存在树结点为0的场景
- 若定义 -1 代表空树,则在判定时需要明确条件
//树1空,树2不空
if(root1==-1 && root2) //错误
if(root1==-1 && root2!=-1) //正确
2. 代码框架(重点)
2.1 采用的数据结构
结构体数组
#define max 10
typedef char ElementType;
struct BinaryTree
{
ElementType data;
int left;
int right;
};
struct BinaryTree Tree1[max],Tree2[max];
2.2 程序主体框架
程序伪码描述
int main()
{
1. 建树并找到树根 :树根就是不被其他结点指向的结点
2. 比较:考虑所有可能(会调用递归)
}
2.3 各分支函数
- CreatTree( ) 根据输入创建树并返回根结点
typedef int RootNumber;
bool flag[max]={0}; //flag数组用于标定root,初始值均为0
RootNumber CreatTree(int Nodes,struct BinaryTree a[]) //建树并找到根
{
char data,left,right; //char类型便于找到空树'-'
for(int i=0;i<Nodes;i++)
{
cin>>data>>left>>right;
a[i].data=data;
if(left=='-')
a[i].left=Null;
else
{
a[i].left=left-'0';
flag[a[i].left]=true;//被指向的元素不是树根,因此改变对应的flag值
}
if(right=='-')
a[i].right=Null;
else
{
a[i].right=right-'0';
flag[a[i].right]=true;
}
}
for(int j=0;j<Nodes;j++)
if(!flag[j]) return j;
}
- Judge( ) 考虑到所有可能就可以AC
(1) 一棵空 一棵不空 1*
(2) 两棵都空 2*
(3) 两棵都不空
I.根不同 3*
II.根相同
i.左子树空,递归右子树 4*
ii.左子树根相同
ii.子树根不同
bool Judge(RootNumber root1,RootNumber root2)
{
if((root1==Null && root2!=Null) || (root1!=Null && root2==Null))
return false;
if(root1==Null && root2==Null)
return true;
if(Tree1[root1].data != Tree2[root2].data)
return false;
else //根相同
{
if(Tree1[root1].left==Null && Tree2[root2].left==Null)
return Judge(Tree1[root1].right,Tree2[root2].right);
else if( Tree1[Tree1[root1].left].data == Tree2[Tree2[root2].left].data)
return ( Judge(Tree1[root1].left,Tree2[root2].left) && Judge(Tree1[root1].right,Tree2[root2].right) ) ;
else if( Tree1[Tree1[root1].left].data != Tree2[Tree2[root2].left].data)
return ( Judge(Tree1[root1].left,Tree2[root2].right) && Judge(Tree1[root1].right,Tree2[root2].left) );
}
}
3. 完整编码
#include <iostream>
using namespace std;
#define max 10
#define Null -1
typedef int OrderNumber;
typedef int RootNumber;
typedef char ElementType;
struct BinaryTree
{
ElementType data;
OrderNumber left;
OrderNumber right;
};
struct BinaryTree Tree1[max],Tree2[max];
bool flag[max]={0};
RootNumber CreatTree(int Nodes,struct BinaryTree a[]);
void Default();
void Display(bool flag[]);
bool Judge(RootNumber root1,RootNumber root2);
int main()
{
int Nodes; int root1,root2; bool result=true;
cin>>Nodes;
if(!Nodes) { cout<<"Yes"<<endl; return 0;}
root1=CreatTree(Nodes,Tree1);
Default();
cin>>Nodes;
root2=CreatTree(Nodes,Tree2);
result = Judge(root1,root2);
if(result) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
RootNumber CreatTree(int Nodes,struct BinaryTree a[]) //建树并找到根
{
char data,left,right; //char类型便于找到空树'-'
for(int i=0;i<Nodes;i++)
{
cin>>data>>left>>right;
a[i].data=data;
if(left=='-')
a[i].left=Null;
else
{
a[i].left=left-'0';
flag[a[i].left]=true;//被指向的元素不是树根,因此改变对应的flag值
}
if(right=='-')
a[i].right=Null;
else
{
a[i].right=right-'0';
flag[a[i].right]=true;
}
}
for(int j=0;j<Nodes;j++)
if(!flag[j]) return j;
}
void Default()
{
for(int i=0;i<max;i++)
flag[i]=false;
}
bool Judge(RootNumber root1,RootNumber root2)
{
if((root1==Null && root2!=Null) || (root1!=Null && root2==Null))
return false;
if(root1==Null && root2==Null)
return true;
if(Tree1[root1].data != Tree2[root2].data)
return false;
else //根相同
{
if(Tree1[root1].left==Null && Tree2[root2].left==Null)
return Judge(Tree1[root1].right,Tree2[root2].right);
else if( Tree1[Tree1[root1].left].data == Tree2[Tree2[root2].left].data)
return ( Judge(Tree1[root1].left,Tree2[root2].left) && Judge(Tree1[root1].right,Tree2[root2].right) ) ;
else if( Tree1[Tree1[root1].left].data != Tree2[Tree2[root2].left].data)
return ( Judge(Tree1[root1].left,Tree2[root2].right) && Judge(Tree1[root1].right,Tree2[root2].left) );
}
}
210929 AC代码:题目的核心问题有两个,数据如何存储(结构体数组) + 如何判定(判定root,左右子树用递归)
进步点:有规划,逻辑思维比较清楚
待改进:做题速度慢 + 粗心 + 编码逻辑能力还需提升
#include <iostream>
using namespace std;
#define Max 10
#define Null -1
struct Node
{
char Value;
int Left;
int Right;
};
int CreateTree(struct Node Tree[],int N);
bool Judge(struct Node Tree01[], int Root01, struct Node Tree02[], int Root02);
int main()
{
struct Node Tree01[Max], Tree02[Max];
int Root01=-1, Root02=-1;
int N;
cin >> N;
Root01 = CreateTree(Tree01, N);
cin >> N;
Root02= CreateTree(Tree02, N);
bool Result = Judge(Tree01, Root01, Tree02, Root02);
if (Result)
cout << "Yes";
else
cout << "No";
return 0;
}
bool Judge(struct Node Tree01[], int Root01, struct Node Tree02[], int Root02)
{
//1.一棵空 一棵不空
if ((Root01 == Null && Root02 != Null) || (Root01 != Null && Root02 == Null))
return false;
//2.两棵都空
if (Root01 == Null && Root02 == Null)
return true;
// 3.两棵都不空:根不同
if (Tree01[Root01].Value != Tree02[Root02].Value) //
return false;
else //4.两棵都不空:根相同, 左左比较、右右比较 或者 左右比较、右左比较
{
int Left01, Left02, Right01, Right02;
Left01 = Tree01[Root01].Left; Right01 = Tree01[Root01].Right;
Left02 = Tree02[Root02].Left; Right02 = Tree02[Root02].Right;
return ( Judge(Tree01, Left01, Tree02, Left02) &&
Judge(Tree01, Right01,Tree02, Right02) )
||
(Judge(Tree01, Left01, Tree02, Right02)&&
Judge(Tree01,Right01,Tree02,Left02));
}
}
int CreateTree(struct Node Tree[], int N)
{
char Value, Left, Right;
bool* a = new bool[N]; //找到TreeRoot
for (int i = 0; i < N; i++)
a[i] = true;
for (int i = 0; i < N; i++) //输入顺序就是结点编号:i=1表示下标为1的结点
{
cin >> Value >> Left >> Right;
Tree[i].Value = Value;
if (Left == '-')
Tree[i].Left = Null;
else
{
Tree[i].Left = Left - '0';
a[Tree[i].Left] = false;
}
if (Right == '-')
Tree[i].Right = Null;
else
{
Tree[i].Right = Right - '0';
a[Tree[i].Right] = false;
}
}
for (int i = 0; i < N; i++)
if (a[i])
return i;
return Null;
}