⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:数据结构初阶
⭐代码仓库:Data Structure
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!
二叉树OJ题
- 一、单值二叉树
- 1、题目描述
- 2、思路及演示
- 3、代码
- 二、相同的树
- 1、题目描述
- 2、思路及演示
- 3、代码
- 三、对称二叉树
- 1、题目描述
- 2、思路及演示
- 3、代码
- 四、二叉树的前序遍历
- 1、题目描述
- 2、思路及演示
- 3、代码
- 五、二叉树的中序遍历&后序遍历
- 1、题目描述
- 2、代码
- 六、另一棵树的子树
- 1、题目描述
- 2、思路及演示
- 3、代码
- 七、二叉树遍历
- 1、题目描述
- 2、思路及演示
- 3、代码
- 总结
一、单值二叉树
1、题目描述
力扣题目来源
2、思路及演示
简单讲讲就是整个树的所有结点的数据都相等即可,需要的是遍历或者递归每个结点进行比较,直到每个结点的值都想等即可,但我们写代码的时候注意遍历一定要是控制返回false的条件,只要是是true的状态就可以继续往后走。遍历结束的条件是到NULL的时候。我们每次进行遍历的时候是先判断左右子树是否存在以及左右子树的值是否跟根节点相同,如果相同则继续遍历,不相同则直接返回false,直到遍历完,遍历完是需要进行递归遍历,是前序遍历的思想,先根再左子树最后再右子树。
3、代码
bool isUnivalTree(struct TreeNode* root){if(root == NULL)return true;if(root->left && root->left->val != root->val)return false;if(root->right && root->right->val != root->val)return false;return isUnivalTree(root->left) && isUnivalTree(root->right);
}
二、相同的树
力扣题目来源
1、题目描述
2、思路及演示
此题我们要准确地找到退出循环的条件,总共有三种情况能够跳出循环,第一种情况是两个树都为空,跳出循环为true;第二种情况是一个树为空,另一个树不为空,跳出循环为false;第三种情况为两棵树相对应的结点的值不相同,跳出循环为false。此题的要点在于使用二叉树的前序遍历进行解决,依旧使用递归,递左子树的结点和右子树的结点判断,直到这两棵树全部遍历完。
这里我们的递归顺序是从下往上的,先比较每个结点的值是否相同,如果不相同则直接返回false,然后再进行递归,先左子树进行递归同样步骤进行判断,比较完左子树再比较右子树即可。
3、代码
bool isSameTree(struct TreeNode* p, struct TreeNode* q){//p和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);
}
三、对称二叉树
1、题目描述
力扣题目来源
2、思路及演示
这里还是一样的思路,找返回条件,总共有三种情况,第一种情况是当左右子树都已经走到NULL的时候,我们返回true即可;第二种情况是左子树结点或者右子树结点有一个到NULL而另一个没有走到NULL,我们返回false;第三种情况是左子树的左结点的值和右子树的右节点不相同我们也返回false或者左子树的右节点的值和右子树的左节点不相同我们也返回false。
3、代码
bool LeftTreeandRightTree(struct TreeNode* left, struct TreeNode* right)
{if(left == NULL && right == NULL)return true;if(left == NULL || right == NULL || left->val != right->val)return false;int lret = LeftTreeandRightTree(left->left, right->right);int rret = LeftTreeandRightTree(left->right, right->left);return lret && rret;
}
四、二叉树的前序遍历
1、题目描述
力扣题目来源
2、思路及演示
我们在之前写过二叉树的前序遍历,但这里我们是需要malloc一个数组将数据的值存入进去,然后再去找左子树、右子树即可。
这里的returnSize是一个输出型函数,作用不是很大,但这个returnSize是需要先算出这棵二叉树的结点个数以后赋到returnSize里面的,我们计算二叉树结点个数是计算左右子树的结点数并相加即可。我们看该题目给的函数的参数是root,returnSize,也就意味着没有数组,我们malloc一个数组出来,这个数组的大小就是我们的TreeSize的个数,我们然后进行传参过去,到了preorder函数里面,是有root,数组a和i的,我们根据前序遍历,先根再左子树再右子树,递归调用的结束标志是空结点。注意,这里的数据是存放到数组当中的:
我们发现非常奇怪的不通过,这怎么调试看起来都没什么问题,既然能通过一部分,肯定是细节出问题了,我们画个图分析一下:
这里我们发现i是局部变量,当我们遇到这种情况的时候,发现i进入两个递归函数后是一样的值,第二个进入的会将第一个进入的覆盖掉,所以,我们使用取地址的i,是将i整个值进行改变,取地址了是找到这个数的地址,访问去改变,不同的递归以后就会进行更改。
3、代码
int TreeSize(struct TreeNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}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);
}int* preorderTraversal(struct TreeNode* root, int* returnSize)
{*returnSize = TreeSize(root);int* a = (int*)malloc(*returnSize * sizeof(int));int i = 0;preorder(root, a, &i);return a;
}
五、二叉树的中序遍历&后序遍历
力扣中序遍历题目来源
力扣后序遍历题目来源
1、题目描述
2、代码
和前面的二叉树的前序遍历一样,我们这里直接写代码:
int TreeSize(struct TreeNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}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 = TreeSize(root);int* a = (int*)malloc(*returnSize * sizeof(int));int i = 0;preorder(root, a, &i); return a;
}
int TreeSize(struct TreeNode* root)
{return root == NULL ? 0:TreeSize(root->left)+TreeSize(root->right)+1;
}void LastOrder(struct TreeNode* root, int* a, int* pi)
{if(root == NULL)return;LastOrder(root->left, a, pi);LastOrder(root->right, a, pi);a[(*pi)++] = root->val;
}int* postorderTraversal(struct TreeNode* root, int* returnSize)
{*returnSize = TreeSize(root);int* a = (int*)malloc(*returnSize * sizeof(int));int i = 0;LastOrder(root, a, &i);return a;
}
六、另一棵树的子树
1、题目描述
力扣题目来源
2、思路及演示
本题的关键思路就是将原本的树的所有子树找出来和SubRoot进行比较,这就用到我们第二个写的判断是否是相同的数的代码了,那我们先ctrl+c,ctrl+v过来。此题的做法就是在大树的每个结点上判断该结点是否和SubRoot相等。
判断两个树是否相等的三个条件为且:1、两个树的根节点的值相等。2、并且两个树的左子树都相等。3、并且两个树的右子树都相等。就是合取的关系:即:1 ^ 2 ^ 3
判断SubRoot是否为大的树的子树的三个条件为或:1、两个树的根节点相等。2、或者SubRoot是大的树的左子树。3、或者SubRoot是大的树的右子树。就是析取的关系。即:1 析取 2 析取 3
3、代码
bool isSameTree(struct TreeNode* p, struct TreeNode* q){//p和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(isSameTree(root, subRoot))return true;return isSubtree(root->left, subRoot)|| isSubtree(root->right, subRoot);
}
七、二叉树遍历
1、题目描述
牛客网题目来源
2、思路及演示
该题的主要思路是先按照先序遍历的思想,分治的思想,即先利用递归构建出一棵二叉树,构建二叉树的重点思想是先malloc根结点再去构建左子树,最后构建右子树即可。题目中数组a给的是#,我们不能将这个#直接放入到二叉树中,那么就碰到#,我们就跳过,返回NULL,找下一个不是#的值,直到遇到NULL即结束。
3、代码
#include <stdio.h>
#include<stdlib.h>typedef struct TreeNode {struct TreeNode* left;struct TreeNode* right;char val;
} TreeNode;//创建一个二叉树
TreeNode* CreatTree(char* a, int* pi)
{if(a[*pi] == '#'){(*pi)++;return NULL;}TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));root->val = a[(*pi)];(*pi)++;root->left = CreatTree(a, pi);root->right = CreatTree(a, pi);return root;
}//中序遍历
void InOrder(TreeNode* root)
{if(root == NULL)return;InOrder(root->left);printf("%c ",root->val);InOrder(root->right);
}int main() {char a[100];scanf("%s", a);int i = 0;TreeNode* root = CreatTree(a, &i);InOrder(root);return 0;
}
总结
二叉树的OJ题层层递进,难度是逐渐上升,我们在把握住每一个OJ题的基础上,同时也需要把握好思想的框架,例如我们第二个的判断相同的树在之后的题目中能够遇到,是需要跳脱出思维的定势的。
家人们不要忘记点赞收藏+关注哦!!!