二叉树相关的基础OJ练习:

整体来说都是偏向简单的基础题

1. 单值二叉树。OJ链接

2. 检查两颗树是否相同。OJ链接

3. 对称二叉树。OJ链接

4. 二叉树的前序遍历。OJ链接

5. 二叉树中序遍历 。OJ链接

6. 二叉树的后序遍历 。OJ链接

7. 另一颗树的子树。OJ链接​​​​​

 


目录

 

一、单值二叉树:OJ链接

二、检查2棵树是否相同:OJ链接

三、对称二叉树:OJ链接

四、二叉树的前序遍历:OJ链接

五、二叉树的中序遍历:OJ链接

六、二叉树的后序遍历:OJ链接

七、另一课树的子树:OJ链接​​​​​


一、单值二叉树:OJ链接

2种方法:一种是遍历一遍二叉树,同时和我们需要的值进行比较

另一种是父亲节点和左孩子右孩子比较,看左右孩子是否和父亲相等,相等则为真

2种方法都要递归实现

 方法一:创建一个子函数。明确这个函数的功能是判断真假(即明确返回类型),递归的结束条件root==NULL   直接前序遍历走一次并判断,先判断根,在判断左子树,在判断右子树  最后返回值 用了一个巧妙的&&,如果左子树为假,就不要判断右子树了  这里用反证法,判断不相等,只要有不相等的就能直接结束递归

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

bool isUnivalTree(struct TreeNode* root) {
    return  _isUnivalTree(root,root->val);
}

方法二:直接递归,这个时候找问题:左孩子和右孩子是否等于父亲结点,子问题也是一样的,大事化小,判断值一样用反证法,如果不等于父亲就返回false

递归展开图,先调左,再调右

 //解决问题:左孩子和右孩子等于双亲结点(父亲结点)
bool isUnivalTree(struct TreeNode* root) {
    if(root==NULL)
    {
        return true;
    }

    //左孩子不为空
    if(root->left&&root->val!=root->left->val)
    {
        return false;
    }
    //右孩子不为空
    if(root->right&&root->val!=root->right->val)
    {
        return false;
    }
    return isUnivalTree(root->left)&&isUnivalTree(root->right);

}

二、检查2棵树是否相同:OJ链接

 看题目大致确定问题:要比较根和根是否相同,左和左,右和右子树是否相同;

递归时,如果左右同时为NULL,就返回真,一个为NULL一个不为NULL,一定不相同,我们不去比较值是否相等,用反证法,只要有不相等情况的就返回假,如果证相等会变麻烦

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    //同时为空返回真
    if(p==NULL&&q==NULL)
    {
        return true;
    }
    //若是没有返回就继续向下,有一个为空就返回假
    if(p==NULL||q==NULL)
    {
        return false;
    }
    //反证,有一个不相等就直接不要证明了
    if(p->val!=q->val)
    {
        return false;
    }
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

三、对称二叉树:OJ链接

看题目是不是有点和上面的题目相似呢,只是变成了左、右子树 对称相等,是不是可以看成,将树分成左、右子树和根。根是相等的,将左子树右子树重新分别看成一颗新的树,不就转化为了2棵树是否相同的问题了吗??只是判断逻辑变了一下 左边的树用它的左子树右边的树右子树比较是否相等,左边的树的右子树要和右边的树的左子树比较是否相等

 创建了一个子函数实现,传根结点的左、右子树上去,在子函数进行比较方便

bool _isSameTree(struct TreeNode* p, struct TreeNode* q) {
    //同时为空返回真
    if(p==NULL&&q==NULL)
    {
        return true;
    }
    //若是没有返回就继续向下,有一个为空就返回假
    if(p==NULL||q==NULL)
    {
        return false;
    }
    //反证,有一个不相等就直接不要证明了
    if(p->val!=q->val)
    {
        return false;
    }
    //这里改成左对右,右对左
    return _isSameTree(p->left,q->right)&&_isSameTree(p->right,q->left);
}

bool isSymmetric(struct TreeNode* root) {
  
    return _isSameTree(root->left, root->right);

}

四、二叉树的前序遍历:OJ链接

 

看函数,知道要返回一个数组,然后明确参数的意义:第一表示根结点,第二个表示元素个数这个形参也可以叫输出型形参,就是在返回时,只能返回一个参数,但是我想改变2个变量,所以可以用一个指针来接受变量地址,修改其值

因为要返回一个数组,明显的我们要创建一个数组,使用malloc去申请一块连续的空间,存放前序遍历的元素,才能返回,不然函数创建的变量只能在函数使用,退出函数就被销毁了

第一步:先计算数据个数,就是二叉树的计算结点个数的函数

第二步:创建一个链表,第一步就是为了第二步准备的,防止空间申请过多或者过少。如下:我们还要一个子函数,通过前序遍历来,将数据放入这个数组内;

第三步:我们发现,当为NULL时。这个数组不放值,不为空就存值进去;当放入值以后,下标也要加随之加1,这里用指针来控制,为了保证下标能改变,不然会出现错误;接着前序遍历就可以了。

 //计算结点个数
int TrSize(struct TreeNode* root)
{
    if(root==NULL)
        return 0;

    return TrSize(root->left)+TrSize(root->right)+1;
}
//pi是下标
void _preorder(struct TreeNode* root, int* a,int*pi)
{
    if(root==NULL)
    {
        return;
    }
    a[(*pi)++] = root->val;
    //前序遍历
    _preorder(root->left,a,pi);
    _preorder(root->right,a,pi);
}
//节点  //returnSize输出型形参
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TrSize(root);
    int* a = (int*)malloc(sizeof(int)*(*returnSize));
    int i = 0;
    _preorder(root,a,&i);
    return a;
}

五、二叉树的中序遍历:OJ链接

 思路一摸一样的,就是数组位置变一下,不做赘述,直接CV修改一下

int TrSize(struct TreeNode* root)
{
    if(root==NULL)
        return 0;

    return TrSize(root->left)+TrSize(root->right)+1;
}
//pi是下标
void _preorder(struct TreeNode* root, int* a,int*pi)
{
    if(root==NULL)
    {
        return;
    }
   
    _preorder(root->left,a,pi);
     a[(*pi)++] = root->val;
    
    _preorder(root->right,a,pi);
    
}



int* inorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TrSize(root);
    int* a = (int*)malloc(sizeof(int)*(*returnSize));
    int i = 0;
    _preorder(root,a,&i);
    return a;
}

六、二叉树的后序遍历:OJ链接

思路和上面的前、中序遍历一样,改数组位置即可!!!真的就是知道一个就能直接解开好几个 

int TrSize(struct TreeNode* root)
{
    if(root==NULL)
        return 0;

    return TrSize(root->left)+TrSize(root->right)+1;
}
//pi是下标
void _preorder(struct TreeNode* root, int* a,int*pi)
{
    if(root==NULL)
    {
        return;
    }
   
    _preorder(root->left,a,pi); 
    
    _preorder(root->right,a,pi);
    a[(*pi)++] = root->val;
}

int* postorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TrSize(root);
    int* a = (int*)malloc(sizeof(int)*(*returnSize));
    int i = 0;
    _preorder(root,a,&i);
    return a;
}

七、另一课树的子树:OJ链接​​​​​

思路:是不是又发现和上面题有点类似,那么这个题目可以分成2个部分来做,树可以分为根和左子树和右子树,那么我用左边的root去走一次前序遍历,然后一旦左边的树的某个根和右边的subRoot相同,就调一次之前的检查二叉树是否相同;

 

bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    //同时为空返回真
    if(p==NULL&&q==NULL)
    {
        return true;
    }
    //若是没有返回就继续向下,有一个为空就返回假
    if(p==NULL||q==NULL)
    {
        return false;
    }
    //反证,有一个不相等就直接不要证明了
    if(p->val!=q->val)
    {
        return false;
    }
    return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root==NULL)
    {
        return false;
    }
    //只有此时根结点相等且树也是相等的才返回true
    if(root->val==subRoot->val&&isSameTree(root,subRoot))
    {
        return true;
    }
    //只要一个为真即可
    return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);

}

 

其实这个是个易错点,不要这样搞,如果这样搞就不对劲了,它只要判断一次不成功就是返回的false,但是左边棵树可以分成很多子树,你都没有遍历完所有结点去比较,你又怎么知道右边的树不是你的子树呢? 所以出错了

完                                 结                 撒                花 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值