目录
题目描述
设计一个算法,判断一棵二叉树是否对称。
来源:2022东北大学软件工程858考研真题第2题
解题思路
方法一(巧妙)
采用层次遍历,先将根节点的左孩子和右孩子入队,用L和R记录出队元素,对比L、R,若值不同,返回0,否则将下一层结点入队,每次入队按照以下顺序:
L的左孩子->R的右孩子->L的右孩子->R的左孩子
这样的入队顺序可以保证L和R出队时保持以根节点为对称轴的相对位置。
方法二(容易想到)
一个很朴素的方法,将二叉树镜像得到一颗新树,再和原二叉树进行比较。可将问题转化为判断两棵二叉树是否完全相同。只是要注意,二叉树镜像之后改变了原树,需要再镜像之前复制一棵树。
将二叉树镜像的算法,其实就是将每个结点的左右子树交换位置。
http://t.csdnimg.cn/nPvkUhttp://t.csdnimg.cn/nPvkU
下面是判断两棵二叉树是否完全相同,使用递归的方法:
T1、T2为传入的两个子树;
若T1、T2都为空,返回1;
若一个空、一个不空,返回0;
若都不空,递归对比T1、T2的左子树和T1、T2的右子树;
宏定义
#define TElemType char
二叉树定义
typedef struct TreeNode{
TElemType val;
struct TreeNode*left;
struct TreeNode*right;
}*Tree,TreeNode;
[注] 上述宏定义和结构体定义是答题时要写的答案,并不是代码实现时的定义,代码实现稍有不同,感兴趣可去“help.h”中查看。
实现函数
- 方法一
int DBDX_2022_2_1(Tree T){
if(!T) return 1; //空树认为是对称的了
SqQueue Q; //定义队列
InitSqQueue(&Q); //初始化队列
EnSqQueue(&Q,T->left); //根左孩子入队
EnSqQueue(&Q,T->right); //根右孩子入队
while(!SqQEmpty(Q)){
TreeNode*L=DeSqQueue(&Q); //每次先出队的入队时都是根节点左子树的孩子
TreeNode*R=DeSqQueue(&Q); //每次先出队的入队时都是根节点右子树的孩子
if(!L&&!R) continue; //L,R都为空
if(!L||!R) return 0; //上一步已经判断两个都为空的情况,所以这个条件语句执行时一定是一个空,一个不空
else{ //只剩一种情况:两个都不为空
if(R->val==L->val){ //值相等,继续往下判断
EnSqQueue(&Q,L->left); //每次入队顺序 左左 右右 左右 右左
EnSqQueue(&Q,R->right);
EnSqQueue(&Q,L->right);
EnSqQueue(&Q,R->left);
}else return 0; //不等时,返回0
}
}
return 1; //全都遍历结束,就是每个结点以跟为对称轴,对面都有一个一样的结点
}
- 方法二
Tree CopyTree(Tree T){ //复制一颗二叉树
Tree T_=(Tree)malloc(sizeof(TreeNode));
T_->ltag=T_->rtag=-1; //不用管,不用做线索二叉树时赋值为-1
if(T){
T_->val=T->val;
T_->left=CopyTree(T->left);
T_->right=CopyTree(T->right);
return T_;
}
return NULL;
}
void TreeSwap(Tree T){ //交换二叉树每个结点的左右子树
if(!T) return;
TreeNode*temp=T->left;
T->left=T->right;
T->right=temp;
TreeSwap(T->left);
TreeSwap(T->right);
}
int isSame(Tree T1,Tree T2){ //判断两颗二叉树是否相同
if(!T1&&!T2) return 1; //T1、T2都为空
else if(!T1||!T2) return 0; //一个空、一个不空
else{ //都不空
if(T1->val!=T2->val) return 0; //结点值不相等
else return isSame(T1->left,T2->left) && isSame(T1->right,T2->right); //判断左右子树是否完全相同
}
}
int DBDX_2022_2_2(Tree T){
if(!T) return 1;
Tree T_=CopyTree(T);
TreeSwap(T_);
return isSame(T,T_);
}
测试代码
#include"../help.h"
/*
题目:设计一个算法,判断一棵二叉树是否对称。
出自:2022东北大学软件工程858考研真题第2题
*/
//方法一
int DBDX_2022_2_1(Tree T){
if(!T) return 1; //空树认为是对称的了
SqQueue Q; //定义队列
InitSqQueue(&Q); //初始化队列
EnSqQueue(&Q,T->left); //根左孩子入队
EnSqQueue(&Q,T->right); //根右孩子入队
while(!SqQEmpty(Q)){
TreeNode*L=DeSqQueue(&Q); //每次先出队的入队时都是根节点左子树的孩子
TreeNode*R=DeSqQueue(&Q); //每次先出队的入队时都是根节点右子树的孩子
if(!L&&!R) continue; //L,R都为空
if(!L||!R) return 0; //上一步已经判断两个都为空的情况,所以这个条件语句执行时一定是一个空,一个不空
else{ //只剩一种情况:两个都不为空
if(R->val==L->val){ //值相等,继续往下判断
EnSqQueue(&Q,L->left); //每次入队顺序 左左 右右 左右 右左
EnSqQueue(&Q,R->right);
EnSqQueue(&Q,L->right);
EnSqQueue(&Q,R->left);
}else return 0; //不等时,返回0
}
}
return 1; //全都遍历结束,就是每个结点以跟为对称轴,对面都有一个一样的结点
}
//方法二
Tree CopyTree(Tree T){ //复制一颗二叉树
Tree T_=(Tree)malloc(sizeof(TreeNode));
T_->ltag=T_->rtag=-1; //不用管,不用做线索二叉树时赋值为-1
if(T){
T_->val=T->val;
T_->left=CopyTree(T->left);
T_->right=CopyTree(T->right);
return T_;
}
return NULL;
}
void TreeSwap(Tree T){ //交换二叉树每个结点的左右子树
if(!T) return;
TreeNode*temp=T->left;
T->left=T->right;
T->right=temp;
TreeSwap(T->left);
TreeSwap(T->right);
}
int isSame(Tree T1,Tree T2){ //判断两颗二叉树是否相同
if(!T1&&!T2) return 1; //T1、T2都为空
else if(!T1||!T2) return 0; //一个空、一个不空
else{ //都不空
if(T1->val!=T2->val) return 0; //结点值不相等
else return isSame(T1->left,T2->left) && isSame(T1->right,T2->right); //判断左右子树是否完全相同
}
}
int DBDX_2022_2_2(Tree T){
if(!T) return 1;
Tree T_=CopyTree(T);
TreeSwap(T_);
return isSame(T,T_);
}
int main(void) {
char arr1[7]={'1','2','2','3','4','4','3'};
Tree T1=Build_tree(arr1,7);
print_arr_c(arr1,7);
show_tree_target(T1,NULL,0);
int res1=DBDX_2022_2_1(T1); //方法一
printf("\nres1:%d\n",res1);
int res1_=DBDX_2022_2_2(T1); //方法一
printf("\nres1_:%d\n",res1_);
char arr2[7]={'1','2','2','3','4','0','3'};
Tree T2=Build_tree(arr2,7);
print_arr_c(arr2,7);
show_tree_target(T2,NULL,0);
int res2=DBDX_2022_2_1(T2); //方法一
printf("\nres2:%d\n",res2);
int res2_=DBDX_2022_2_2(T2); //方法二
printf("\nres2_:%d\n",res2_);
return 0;
}