👑作者主页:@进击的安度因
🏠学习社区:进击的安度因(个人社区)
📖专栏链接:数据结构
文章目录
- 一、二叉树的最小深度
- 二、单值二叉树
- 三、相同的树
- 四、另一棵树的子树
- 五、翻转二叉树
- 六、对称二叉树
- 七、二叉树的前序遍历
- 八、平衡二叉树
今天为大家带来了
leetcode
中的八道二叉树OJ题,让我们一起刷题吧!
一、二叉树的最小深度
链接:111. 二叉树的最小深度
描述:
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
示例1:
输入:root = [3,9,20,null,null,15,7]
输出:2
示例2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
提示:
- 树中节点数的范围在
[0, 105]
内 -1000 <= Node.val <= 1000
思路:
这里的二叉树有两种情况 同时具有左右子树 和 只具有左子树或右子树。
对于 同时具有左右子树的情况 非常简单,可以借鉴上篇文章中我们求最大深度的接口的思路。
我们递归遍历到最后一层,返回较小的高度 + 1。每次遍历分别计算左右子树的高度,防止多次递归调用。
如果遇到 NULL
则返回0。
对于 只有单边子树 的情况就比较麻烦了,由于没有一边的子树,我们肯定是返回有子树的一边的高度。
但是如果套用 同时具有左右子树的情况 就不行了,到时候本来应该返回有子树的那一边,但是返回的总是1。
所以需要 两种情况分别处理 :
-
首先求出左右子树高度和 左右子树中的最小高度。
-
如果 当前树有左右子树 则返回最小高度 + 1;
-
如果 当前树的左右子树为空 那么就返回
左边高度 + 右边高度 + 1
,因为 有一边的高度必定为0,所以加上的高度并不影响正确结果。
二、单值二叉树
链接:965. 单值二叉树
如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回 true
;否则返回 false
。
示例1:
输入:[1,1,1,1,1,null,1]
输出:true
示例 2:
输入:[2,2,2,5,2]
输出:false
提示:
- 给定树的节点数范围是
[1, 100]
。 - 每个节点的值都是整数,范围为
[0, 99]
。
思路:
单值树就是每个节点的值都相同。
而一棵树是单值树,那么它的 根节点的值和左右子树的值一定相同,那么就可以拆分成子问题。
接下来判断各种情况:
-
如果 节点为空 ,那么是单值二叉树,返回真;
-
如果 左子树不为空且值 和 根节点值不相等 ,返回假;
-
如果 右子树不为空且值 和 根节点值不相等, 返回假;
就这样分别递归左右子树,即可判断是否为单值二叉树。
三、相同的树
链接:100. 相同的树
描述:
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
示例 1:
输入:p = [1,2,3], q = [1,2,3]
输出:true
示例2:
输入:p = [1,2], q = [1,null,2]
输出:false
示例3:
提示:
- 两棵树上的节点数目都在范围
[0, 100]
内 -104 <= Node.val <= 104
思路:
判断两棵树是否相等,我们先思考一下如何才算相等:
-
对于 两棵空树 ,必定相等,返回真;
-
如果 一棵树空,另一棵树不为空 ,那么不相等,返回假;
-
如果 树中值相同但是结构不同 ,那么也不相等,返回假;
-
如果 两棵树对应节点的值不相等,那么也不相等,返回假;
那么只要分别递归两棵的左右子树,如果一直递归到底都是真,且左右子树返回的结果都为真,那么这两棵树就相同。
四、另一棵树的子树
链接:572. 另一棵树的子树
描述:
给你两棵二叉树 root
和 subRoot
。检验 root
中是否包含和 subRoot
具有相同结构和节点值的子树。如果存在,返回 true
;否则,返回 false
。
二叉树 tree
的一棵子树包括 tree
的某个节点和这个节点的所有后代节点。tree
也可以看做它自身的一棵子树。
示例1:
输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true
示例2:
输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false
提示:
root
树上的节点数量范围是[1, 2000]
subRoot
树上的节点数量范围是[1, 1000]
-10^4 <= root.val <= 10^4
-10^4 <= subRoot.val <= 10^4
思路:
这道题算是 相同的树 的进阶版,如果没有我们上一题的铺垫,这题会写的很烦。
我们的主体思路是判断 二叉树的每一棵子树是否和 subRoot
相等。
那么给出我们主要决策:
- 由于
subRoot
一定不为空,所以一旦root
的子树为空,则返回假; - 如果
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;
}
// 如果找到了子树,则直接返回
if (isSameTree(root, subRoot))
{
return true;
}
// 分别递归左右子树,子树只要找到了,则存在
return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
五、翻转二叉树
链接:226. 翻转二叉树
描述:
给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。
示例1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
示例2:
输入:root = [2,1,3]
输出:[2,3,1]
示例3:
输入:root = []
输出:[]
提示:
- 树中节点数目范围在
[0, 100]
内 -100 <= Node.val <= 100
思路:
对于翻转二叉树,就是把一棵树所有的左右子树对调,这里我们可以使用 后序遍历 的思想。
那么我们基本就可以写出我们的思路:
如果节点为空,那么无需翻转,返回 NULL
;
否则就先递归左子树,再递归右子树,到底后,开始交换左右子树,最后逐层返回。
六、对称二叉树
链接:101. 对称二叉树
描述:
给你一个二叉树的根节点 root
, 检查它是否轴对称。
示例1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例2:
输入:root = [1,2,2,null,3,null,3]
输出:false
提示:
- 树中节点数目在范围
[1, 1000]
内 -100 <= Node.val <= 100
思路1:
一棵树是否轴对称,可以理解为 它的根节点的 某一边子树翻转
后是否和另一边为 相同的树
。
比如我们这边翻转左子树:
我们上方 相同的树 和 翻转二叉树 刚刚写过,那么写这种思路就很简单了。
这题我们也给出相应的决策:
- 如果左右子树都为空,为根节点,那么返回真;
- 如果左右子树一遍不为空,那么返回假;
- 如果左右子树都不为空,那么给定
left
和right
分别记录左右子树。翻转左边,记录右边; - 最后返回判断左右子树是否为相同的树。
接下来我们看看代码怎么写:
struct TreeNode* invertTree(struct TreeNode* root)
{
if (root == NULL)
{
return NULL;
}
struct TreeNode* left = invertTree(root->left);
struct TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
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 isSymmetric(struct TreeNode* root)
{
// 左右子树都为空
if (root->left == NULL && root->right == NULL)
{
return true;
}
// 左右子树一边不为空
if (root->left == NULL || root->right == NULL)
{
return false;
}
// 左右子树两边都不为空
struct TreeNode* left, *right;
if (root->left && root->right)
{
// 翻转左边
left = invertTree(root->left);
right = root->right;
}
// 和右边子树比较,返回 bool 值
return isSameTree(left, right);
}
但是这种方法也是不太好的,因为破坏了原本二叉树的结构,那么还有更好的方法吗?看思路2。
思路2:
一棵树对称就是 它的左右子树呈镜像状态 。说白了就是节点左子树的值等于右子树的值,右子树的值等于左子树的值。
那么我们可以用一个 check
函数来递归检查,并将二叉树的根节点传两份过去:
- 如果两棵树都为空,返回真;
- 如果两棵树一棵为空,另一棵不为空,返回假;
- 如果两棵树都不为空,但是值不相等,返回假;
- 上面的走完都没有返回,那么就分别递归第一棵树的左子树和第二棵树的右子树;第一棵树的右子树和第二棵树的左子树。
最后返回 check
函数的真值,就能判断出结果。
七、二叉树的前序遍历
链接:144. 二叉树的前序遍历
描述:
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:
输入:root = []
输出:[]
示例3:
输入:root = [1]
输出:[1]
示例 4:
输入:root = [1,2]
输出:[1,2]
示例 5:
输入:root = [1,null,2]
输出:[1,2]
提示:
- 树中节点数目在范围
[0, 100]
内 -100 <= Node.val <= 100
思路:
这里的前序遍历和之前的有所不同,题目要求我们将遍历结果存到数组中,将数组返回,且空指针不需要记录。
那么我们可以计算出二叉树的大小,然后 动态开辟一个二叉树大小的数组。
并使用一个下标来记录数组的元素个数,最后 前序遍历二叉树 ,将结果存入数组,返回数组。
相关题目:
- 94. 二叉树的中序遍历
- 145. 二叉树的后序遍历
相关题目和这道题思路类似,我就不带着大家看了,大家自己下去可以试试。
八、平衡二叉树
链接:110. 平衡二叉树
描述:
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例3:
输入:root = []
输出:true
提示:
- 树中的节点数在范围
[0, 5000]
内 -104 <= Node.val <= 104
思路:
所谓 平衡二叉树就是任意节点的子树的高度差都小于等于1。
基于这个理解,那么我们可以将它分成子问题:每个节点的子树的高度差小于等于1。
那么就可以给出思路:
- 如果节点为空,则是完全二叉树;
- 否则就求左右两边子树高度;
- 再判断左右子树的 高度差的绝对值 是否 大于1 ,大于1则一定不是完全二叉树,返回假;
- 最后分别递归左右子树,判断左右子树是否满足完全二叉树的条件。
求高度的就是我们上篇博客的 计算二叉树的高度 的接口。
完整代码:
int TreeHeight(struct TreeNode* root)
{
if (root == NULL)
{
return 0;
}
int leftHeight = TreeHeight(root->left);
int rightHeight = TreeHeight(root->right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool isBalanced(struct TreeNode* root)
{
// 根节点为空,是完全二叉树
if (root == NULL)
{
return true;
}
// 求左右两边高度
int leftHeight = TreeHeight(root->left);
int rightHeight = TreeHeight(root->right);
// 绝对值大于1一定不是完全二叉树
if (abs(leftHeight - rightHeight) > 1)
{
return false;
}
return isBalanced(root->left) && isBalanced(root->right);
}
到这里,本篇博客就到此结束了。二叉树的题目总体来说代码还是很少的,主要是递归的思路。大家可以下去多画画图!
如果觉得anduin写的还不错的话,还请一键三连!如有错误,还请指正!
我是anduin,一名C语言初学者,我们下期见!