emmm,不刷树的题目还真以为自己有多了解树。。。题目一做,方纸上得来终觉浅啊,才知道自己的浅薄和学海的浩瀚。
那,我们来看看这次遇到些什么问题
文章目录
前情概要
在做树相关题目时,我觉得基础还是要先牢靠一点的:
二叉树的前中后序遍历
题目(1):探寻树的深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目(2):验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/validate-binary-search-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目(3):验证对称树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
进阶:
你可以运用递归和迭代两种方法解决这个问题吗?
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/symmetric-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的题解(1)
这题实在简单的不想说什么了,连流程图我都不想画,直接上代码吧。
int maxDepth(TreeNode* root) {
if(root == NULL)
return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
简单吧,三行代码的事情。。。
或者说,一行代码的事情而已。。。
我的题解(2)
这题真的打破了我对树的幻想。我一直以为二叉搜索树我还挺熟的,没想到,居然处处碰壁。。。
刚开始我采用层序遍历的方式,一层层比对,(当前节点小于当前右节点,大于左节点),但是很快就发现问题了。
如果觉得上面的那个方式没错,那么看完这张图就不会这么想了吧。
当时看到这个情况,我也是整个人都不好了,不仅仅要和子节点比对,孙子节点还要防着。。。
经过一早上的挣扎,我放弃了前序遍历,果然是我基础不牢,这个模型其实可以用中序遍历来弄,可惜我就浪费了几个小时没想到。
对一颗正常的二叉搜索树,用中序遍历打印出来的节点顺序是严格从一端到另一端的。
当时也想着直接取值比对,但终究是递归也不太熟,最后只好把值都放到数组里在遍历数组比对了。
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
bool MidOrderTraverse(TreeNode* T, vector<int>& temp)
{
if (T == NULL)
return true;
MidOrderTraverse(T->left,temp);
temp.push_back(T->val);
cout << T->val << " ";
MidOrderTraverse(T->right,temp);
for (int i = 0; i < temp.size()-1; i++)
{
int j = i + 1;
if (temp[i] >= temp[j])
return false;
}
return true;
}
bool isValidBST(TreeNode* T)
{
vector<int> temp;
int k = MidOrderTraverse(T, temp);
return k;
}
最后就做成这个样子了。
我的题解(3)
经过第二题,我对递归又深入了解了一点,知道该把一些希望固定住的值固定住,可以用参数形式,也可以用static.
那这题其实就很简单了,只要把树从根节点左右对半开,然后比较左右两颗子树就好,比较子树的话,前中后序都可以,不过需要做一点小改变:
我用的是正反前序遍历同步的方法
int Pre(TreeNode* Tl, TreeNode* Tr,int flag) //flag为标志退出循环
{
if (flag == 0)
return false;
if (Tl == NULL)
{
if(Tr == NULL)
return true;
return false;
}
if (Tr == NULL)
return false;
if (Tl->val != Tr->val)
{
flag = 0;
return false;
}
flag = Pre(Tl->left,Tr->right,flag);
if (flag == 0)
return false;
flag = Pre(Tl->right,Tr->left,flag);
if (flag == 0)
return false;
return true;
}
bool isSymmetric(TreeNode* root)
{
//先排除极端情况
if (root == NULL)
return true;
if (root->left == NULL)
{
if (root->right != NULL)
return false;
return true;
}
if (root->right == NULL)
return false;
if (root->left->val != root->right->val)
return false;
int ret = Pre(root->left, root->right,1);
return ret;
}
看着挺多的,其实四分之三的篇幅都用在了对特殊情况的剔除上。真正匹配的代码是这里:
int Pre(TreeNode* Tl, TreeNode* Tr,int flag) //flag为标志退出循环
{
if (flag == 0)
return false;
flag = Pre(Tl->left,Tr->right,flag);
if (flag == 0)
return false;
flag = Pre(Tl->right,Tr->left,flag);
if (flag == 0)
return false;
return true;
}
官方题解(1)略
官方题解(2)
方法一: 递归
上述思路可以用递归法实现。首先将结点的值与上界和下界(如果有)比较。然后,对左子树和右子树递归进行该过程。
class Solution {
public boolean helper(TreeNode node, Integer lower, Integer upper) {
if (node == null) return true;
int val = node.val;
if (lower != null && val <= lower) return false;
if (upper != null && val >= upper) return false;
if (! helper(node.right, val, upper)) return false;
if (! helper(node.left, lower, val)) return false;
return true;
}
public boolean isValidBST(TreeNode root) {
return helper(root, null, null);
}
}
> 作者:LeetCode
> 链接:https://leetcode-cn.com/problems/validate-binary-search-tree/solution/yan-zheng-er-cha-sou-suo-shu-by-leetcode/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
复杂度分析
时间复杂度 : O(N)。每个结点访问一次。
空间复杂度 : O(N)。我们跟进了整棵树。
官方题解(3)
和我的差不多,迭代的就不说了。
总结
树!!!
我也不多说,心照不宣