提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
作者:小树苗渴望变成参天大树
作者宣言:认真写好每一篇博客
作者gitee:gitee
如 果 你 喜 欢 作 者 的 文 章 ,就 给 作 者 点 点 关 注 吧!
树的oj题
- 前言
- 一、[单值二叉树](https://leetcode.cn/problems/univalued-binary-tree/)
- 二、[相同的树](https://leetcode.cn/problems/same-tree/)
- 三、[翻转二叉树](https://leetcode.cn/problems/invert-binary-tree/)
- 四、[另一棵树的子树](https://leetcode.cn/problems/subtree-of-another-tree/)
- 五、[对称二叉树](https://leetcode.cn/problems/symmetric-tree/)
- 六、[二叉树的前序遍历](https://leetcode.cn/problems/binary-tree-preorder-traversal/)
- 七、[二叉树的遍历](https://www.nowcoder.com/practice/4b91205483694f449f94c179883c1fef?tpId=60&&tqId=29483&rp=1&ru=/activity/oj&qru=/ta/tsing-kaoyan/question-ranking)
- 八、总结
前言
今天出一期关于二叉树的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;
}
这题到这就完成了,如果大家看来我上一篇写的关于二叉树的操作博客,这些题目其实都大同小异。
八、总结
博主今天画了好久时间为大家总结关于二叉树相关的题目,第一,是讲自己学到的知识,通过题目来联系,第二,讲学到的知识和大家一起分享,一起交流,也可以是自己记的更加的久,希望大家可以八这些题目弄懂,知识学到就是自己的,希望你们都可以学到知识,我们一起进步,那今天的分享就到此结束,我们下篇再见