前面我们,稍微介绍了一下二叉树的结构,本章我的目的在于复习一下,二叉树的基本例题,巩固知识,由于我的二叉树基本用法还没有学完,所以先更新二叉树的基本题目,在下一篇文章,我将具体介绍二叉树的基本用法,本节先介绍一些题目,大家可以等二叉树的基本用法写完之后,再来看这一篇。
目录
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);
}
总结:
二叉树的基本问题,很多都是利用递归原理去实现的,这个是双路递归,即进行两个方向的递归,我们需要清楚的理解递归逻辑,如果无法理解递归,那么就花一些时间,画出递归的展开图去理解,多次下来就能提高对递归的认识,与此同时,要及时的去复习和巩固。