整体来说都是偏向简单的基础题
1. 单值二叉树。OJ链接
2. 检查两颗树是否相同。OJ链接
3. 对称二叉树。OJ链接
4. 二叉树的前序遍历。OJ链接
5. 二叉树中序遍历 。OJ链接
6. 二叉树的后序遍历 。OJ链接
7. 另一颗树的子树。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,但是左边棵树可以分成很多子树,你都没有遍历完所有结点去比较,你又怎么知道右边的树不是你的子树呢? 所以出错了
完 结 撒 花