二叉树基础OJ题目

前面我们,稍微介绍了一下二叉树的结构,本章我的目的在于复习一下,二叉树的基本例题,巩固知识,由于我的二叉树基本用法还没有学完,所以先更新二叉树的基本题目,在下一篇文章,我将具体介绍二叉树的基本用法,本节先介绍一些题目,大家可以等二叉树的基本用法写完之后,再来看这一篇。

目录

1.单值二叉树

思路:

 代码实现

2.相同的树

思路:

代码实现

3.对称二叉树

思路:

代码实现

4.二叉树的前序遍历

思路

代码实现

5.另一棵树的子树

思路:

代码实现

总结:


1.单值二叉树

本题目要求我们,判断一棵树的所有节点的值都是相同的值,要知道二叉树的题目大多都是用递归来实现,我们要以递归的思想来进行分析题目

思路:

首先我们要判断根节点是否为空,如果为空,那么空树肯定为单值二叉树,因此返回true

若根节点不为空,那么就要判断根节点的值和左子树,右子树的节点的值进行比较,若不相同则返回false,在此之前,首先要判断左子树和右子树是否为空,若为空就不用进行比较。

那么我们就可以将此类问题,分为主问题和子问题

如结构图所示,我们可以将问题拆分为多个子问题,首先判断的是红色框内,按照上面我们的思路进行实现,然后递归至它的左右子树,让左右子树再判断自身是否为单值二叉树即可

 代码实现

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);
    
}

2.相同的树

思路:

这道题的描述,就是判断两棵树是否完全相同,即结构相同,值相同,我知道会有人想用二叉树的前序遍历,将其存到数组中,然后再一一比较,但是这是不行的,如图所示

我们可以发现,两棵树的前序遍历结果是一样的都是{1,2,3},但是二者的结构不同,所以我们不能用前序遍历进行判断。

那我们如何去实现呢?我们按照上一道题的想法进行剖析,我们发现判断两棵树是否相同,也可以拆分为子问题,我们可以将两棵树的子树进行判断是否相同,这样就又用递归将问题拆分,进行解决

当两棵树同时为空的时候,则返回true,很显然是相同的结构

当两棵树有一颗为空时,由于上面的条件,肯定另一颗不为空,那么就返回false

然后我们比较根节点的值,如果不相同则返回false

再利用递归进行实现

代码实现

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);

}

3.对称二叉树

这道题目的描述,和同一棵树的逻辑大差不差,就是将一棵树分为两个子树,然后左子树等于右子树这样的对称比较,总体思路和判断相同的树类似,只是有些许差别。

思路:

我们仍然利用递归,由于他给的只是根的节点,因此我们需要再添置一个函数,让函数参数变为两个,然后我们再利用递归,将同一颗树中的结构和值进行比较

注意:由于这是同一颗树的对称比较,所以我们在比较子树的时候,要比较传入右子树和左子树进行比较

比较思路依然与上题类似,大家看一下代码,应该就能够理解

代码实现

 bool _isSymmetric(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 _isSymmetric(p->right,q->left)&&
    _isSymmetric(p->left,q->right);
 }
bool isSymmetric(struct TreeNode* root) {
    return _isSymmetric(root->left,root->right);
}

4.二叉树的前序遍历

由题目描述我们可知 ,这就是前序遍历,但是我们在二叉树的基本用法中,将用打印的方式呈现,而这道题则是将其存入到数组中来实现。

思路:

我们首先需要创建一个数组,因此我们需要知道树的大小,用TreeSize递归进行实现,然后我们就可以开始构建数组了,前序遍历就是根-左子树-右子树的遍历顺序,我们还需要用CreateArr函数来进行传入数据,由于我们需要记录数组下标,而在递归中形参的值不会保存,所以我们传入变量的地址,通过修改地址进行保存数据,我们判断当root为空则走到树的末尾,即返回,若不为空就放到数组中,然后让下标+1,再利用递归去寻找左子树和右子树。

代码实现

 typedef struct TreeNode TreeNode;
 void createArr(int* a,TreeNode* root,int* sz)
 {
    if(root == NULL)
    {
        return;
    }
    a[*sz] = root->val;
    (*sz)++;
    createArr(a,root->left,sz);
    createArr(a,root->right,sz);

 }
 int TreeSize(struct TreeNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    return TreeSize(root->left) + TreeSize(root->right)+1;
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
    *returnSize = TreeSize(root);
    int* a=(int*)malloc(sizeof(TreeNode)*(*returnSize));
    int i = 0;
    createArr(a,root,&i);
    return a;
}

下面中序遍历和后序遍历和这道题的逻辑类似就是顺序有变化,大家可以尝试把这两道题写一下

二叉树的中序遍历

二叉树的后序遍历

5.另一棵树的子树

 这道题,是让我们找到root是否包含和subRoot具有相同结构和值的子树

思路:

我们还是利用递归进行实现,将这个大问题用递归拆分为多个小问题,我们这里还需要用之前相同的树的函数进行简便的判断

我们先判断root是否为空,为空就返回false,作为递归的终止条件

然后实现逻辑,我们判断根的值是否相同,如果相同才说明有可能符合题目的要求,然后我们利用isSameTree进行判断,记录返回值,若为True就返回,如果是false,那么就进行递归,去寻找左子树,右子树,让其作为根继续比较

注意:这里最后的判断条件是||,这样利用短路条件,当找到左子树符合的结果后,就不用再去寻找右子树,大大减少了逻辑运算量

代码实现

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;
    if(root->val== subRoot->val)
    {
       bool ret =  isSameTree(root,subRoot);
       if(ret)
       return ret;
    }

    return isSubtree(root->left,subRoot)||
    isSubtree(root->right,subRoot);

}

总结:

二叉树的基本问题,很多都是利用递归原理去实现的,这个是双路递归,即进行两个方向的递归,我们需要清楚的理解递归逻辑,如果无法理解递归,那么就花一些时间,画出递归的展开图去理解,多次下来就能提高对递归的认识,与此同时,要及时的去复习和巩固。

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值