【数据结构】-七个题目带你更深的理解二叉树

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee
在这里插入图片描述
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!


前言

今天出一期关于二叉树的oj题,带大家更好的体会二叉树的递归,也能更好的为后面的高阶数据结构打好基础。


一、单值二叉树

我们来看一下题目的描述:
在这里插入图片描述
在这里插入图片描述
我们来看一下代码:

bool isUnivalTree(struct TreeNode* root){
    
    
   if(root==NULL)
        return true;
    if(root->left!=NULL&&root->val!=root->left->val)
        return false;
    if(root->right!=NULL&&root->val!=root->right->val)
        return false;   
    return isUnivalTree(root->left)&&isUnivalTree(root->right); 
}

接下来我画一下递归展开图,后面的题目我将不在画了,道理都一样,不理解的自己去尝试画一下:
在这里插入图片描述

我们可以很清楚的看到时怎么一层一层的递归下去,然后再返回回来,有错误直接返回,不在进行往下一层的判断了

二、相同的树

我们来看题目和举例:在这里插入图片描述
我们来看这一题的图解:
在这里插入图片描述
我们来看代码:

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    
    
    if((p==NULL&&q!=NULL)||(p!=NULL&&q==NULL))
        return false;
    if(p==NULL&&q==NULL)
        return true;
    if(p->val!=q->val)
        return false;
    bool left=isSameTree(p->left,q->left);
    bool right=isSameTree(p->right,q->right);
    return left&&right;
}

思想都是遇到不相等的时候就开始往回返,因为相等往下面走没有意义,反正都要递归下去的

三、翻转二叉树

看题目:
在这里插入图片描述
我们来看这道题的图解:

再题目第一句就说交换此结点的左走指针然后将此结点的进行返回
在这里插入图片描述
我们来看代码:

struct TreeNode* invertTree(struct TreeNode* root){
    
    
    if(root==NULL)
       return NULL;
    struct TreeNode*tmp=root->left;
    root->left=root->right;
    root->right=tmp;//交换指针
    invertTree(root->left);//左子树交换指针
    invertTree(root->right);//右子树交换指针
    return root;//返回此结点
}

我们学完上面三个题目,就可以再做一些通过这三个题目的复用就可以更好的解题

四、另一棵树的子树

我们来看题目的原型:

在这里插入图片描述

说明:从此结点往下所有结点和子树是相同的才行,中间一块和子树相同时不对的,就好比示例二
来看图解:

在这里插入图片描述
我们来看代码:

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    
    
    if((p==NULL&&q!=NULL)||(p!=NULL&&q==NULL))
        return false;
    if(p==NULL&&q==NULL)
        return true;
    if(p->val!=q->val)
        return false;
    bool left=isSameTree(p->left,q->left);
    bool right=isSameTree(p->right,q->right);
    return left&&right;
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    
    
     if(root==NULL)
            return false;
     if(isSameTree(root,subRoot))
            return true;
     return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}

复用了之前相同的树的代码。希望大家可以理解

五、对称二叉树

我们来看一下这个题目的原型:
在这里插入图片描述
我们看到这个题目是判断同一棵树是否轴对称,因为上面刚做过相同的说,我就想相同的树是走同一个方向判断值是不是相等,那么对称不就是走相反的方向吗?但调试之后发现不行,这是再同一棵树中,这种办法行不通。
接下来看另一种思路的图解:
在这里插入图片描述
其实这题再有了前面两题的基础上,跟递归就没有关系,完全在于思路的理解上,我们来看代码:

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    
    
    if((p==NULL&&q!=NULL)||(p!=NULL&&q==NULL))
        return false;
    if(p==NULL&&q==NULL)
        return true;
    if(p->val!=q->val)
        return false;
    bool left=isSameTree(p->left,q->left);
    bool right=isSameTree(p->right,q->right);
    return left&&right;
}

struct TreeNode* invertTree(struct TreeNode* root){
    
    
    if(root==NULL)
       return NULL;
    struct TreeNode*tmp=root->left;
    root->left=root->right;
    root->right=tmp;
    invertTree(root->left);
    invertTree(root->right);
    return root;
}
bool isSymmetric(struct TreeNode* root){
    
    
   if(root==NULL)
        return true;
    struct TreeNode*left=root->left;
    invertTree(left);//翻转
    return isSameTree(left,root->right);//判断是否相同即判断是否对称
}

通过这几题我们发现大部分规律都是一样的,我们把其中一两道题完全弄懂,其他的题目也就迎刃而解了,接下来难度会可能不会有啥太大的变化,但是解题思路会发生一点变化

六、二叉树的前序遍历

我们来看一下题目的原型:
在这里插入图片描述

乍一看这个题目不就是前序遍历吗?但是把前序遍历的代码直接拿过来发现通过不了,而且还发现了一个用不上的参数,我们看看题目默认就有的函数:在这里插入图片描述

其实这题的具体本意是把遍历的数据放到一个数组里面,因为再力扣上,返回一个数组是不行,还必须返会数组里面数据的个数,因为是C语言一次智能返回一个值,所以使用指针的方法来访问数组里面元素的个数

一、那我们来看看这题怎么写,首先得创建一个数组,来存储数据,数组的大小就是树的总结点个数,之前在二叉树的博客中介绍过怎么计算总结点数:

int treeSize(struct TreeNode* root)
{
    
    
    if(root==NULL)
        return 0;
    return treeSize(root->left)+treeSize(root->right)+1;
}

我们开始创建一个数组,然后写一个前序遍历的函数将数据保存到数组中:

void preTree(struct TreeNode*root,int *a)
{
    
    
    if(root==NULL)
        return;
    int i=0;
    a[i++]=root->val;
    preTree(root->left,a);
    preTree(root->right,a);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
    
      
     *returnSize=treeSize(root);
    struct TreeNode*a=(struct TreeNode*)malloc(sizeof(struct TreeNode)*treeSize(root));
    preTree(root,a);
    return a;
}

前序遍历就是把数据的打印换成的保存到数组里面了,相信大家应该可以理解。
我们看运行结果:
在这里插入图片描述

我们清晰的看到出现了随机值,凭我们的经验加上之前数组的知识,这可能就是数组的月结访问,我们看到树里面应该有三个结点,但是出现了两个随机值说明得到数组里面的个数是正确,唯一的问题是数组后面两个位置没有保存到结点,而且按照前序遍历的特点,第一个元素应该是1,第二个元素是2,第三个元素应该是3,但为什么第一个打印出来是3呢?原因只有一个,在进行保存的时候,i没有往后面走,导致前面的数一直被后面的数覆盖

出现问题的原因非常的简单,就是行参是实参的一份临时拷贝,形参的改变不会影响实参,所以我们需要传地址,因为这是递归,所以在一开始就需要传一个i进去,我们来看最后正确代码:

int treeSize(struct TreeNode* root)
{
    
    
    if(root==NULL)
        return 0;
    return treeSize(root->left)+treeSize(root->right)+1;
}
void preTree(struct TreeNode*root,int *a,int *i)
{
    
    
    if(root==NULL)
        return;
    a[(*i)++]=root->val;
    preTree(root->left,a,i);
    preTree(root->right,a,i);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
    
      
     *returnSize=treeSize(root);
    struct TreeNode*a=(struct TreeNode*)malloc(sizeof(struct TreeNode)*treeSize(root));
    int i=0;
    preTree(root,a,&i);
    return a;
}

大家看到这里对这题应该理解了吧,对于这个还有类似的题目,二叉树的中序遍历,二叉树的后序遍历
大家可以自己去练练手,我们开始将下一道题

七、二叉树的遍历

相信大家看到这个的时候,可能都会感到迷茫,为什么又是遍历?,大家不要感到不懂,我将会一一的为大家解答问题的

在二叉树的博客,我主要介绍了三种递归遍历二叉树,前序中序后序,在讲解每一个遍历之前我都先为大家画遍历序列,让大家好理解,今天我就带大家通过遍历序列来画二叉树,在来去解决这个题目

一、 带NULL结点的遍历序列,我将以前序序列为例为大家讲解(从上往下,从左往右看图解):
在这里插入图片描述

带NULL结点可以直接什么时候结束什么时候开始遍历右子树,所以知道其中一个序列就行了。

二、不带NULL结点的序列,由于不知道啥时候结束往下面遍历,所以一个序列肯定不够,所以我们需要通过两个及两个以上序列才能画出二叉树

前后,前中,后中,前中后,有这四种组合,这四种难道都可以吗??

答案是除了前后,其余三种都是可以的,我就不给大家讲为什么不行,大家下来可以自己弄两个前后序列去试试,我将讲一种可以的组合,(前中):

在这里插入图片描述
这个案例非常简单,方便大家理解,你们可以自己去找找一些案例按照这个方法去做做,接下来我就要正式讲解题目了

我们来看看题目的原型:
在这里插入图片描述这题目的主要任务是完成创建二叉树,其余的都好办,前序遍历的特点是,先根,在左,在右,那么我们在创建结点的时候,不也按照这个方法给结点里面赋值,有了这个思路,我们来看代码:我们先定义一个二叉树

typedef char BTDateType;//结点里面的值都是字符类型
typedef struct BTree
{
    
    
	BTDateType data;
	struct BTree* left;
	struct BTree* right;
}BTNode;

在第六题的时候时把二叉树结点里面的值放到数组里面,现在是把数组里面的值放到结点里面,遍历数组也需要传地址
我们看创建二叉树的代码:

BTNode*CreateTree(char*s,int*i)
{
    
    
    if(s[*i]=='#')//遇到‘#’就返回空
    {
    
    
        (*i)++;//这个++必须放在这里,放在[]这里不管条件为真还是为假,都++了
        return NULL;
    }
    BTNode*root=(BTNode*)malloc(sizeof(BTNode));
    root->data=s[(*i)++];
    root->left=CreateTree(s, i);
    root->right=CreateTree(s, i);
    return root;
}

看本质就是和二叉树的前序遍历一样,大家可以在自己的编译器下测试其他的,输入中序或者后序,那构建二叉树也类似于中序或者后序的方式。大家可以自己下去尝试一下

接下来完成中序遍历二叉树就可以了:

void Inorder(BTNode*root)
{
    
    
    if(root==NULL)
        return;
    Inorder(root->left);
    printf("%c ",root->data);
    Inorder(root->right);
}

我们在加上输入字符串序列的主函数:

int main() {
    
    
    char s[100]="\0";
    scanf("%s",s);
    int i=0;
    BTNode*root=CreateTree(s,&i);
    Inorder(root);
    return 0;
}

这题到这就完成了,如果大家看来我上一篇写的关于二叉树的操作博客,这些题目其实都大同小异。

八、总结

博主今天画了好久时间为大家总结关于二叉树相关的题目,第一,是讲自己学到的知识,通过题目来联系,第二,讲学到的知识和大家一起分享,一起交流,也可以是自己记的更加的久,希望大家可以八这些题目弄懂,知识学到就是自己的,希望你们都可以学到知识,我们一起进步,那今天的分享就到此结束,我们下篇再见
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_69369227/article/details/129896020
今日推荐