文档讲解:代码随想录 (programmercarl.com)
视频讲解:二叉树的高度和深度有啥区别?究竟用什么遍历顺序?很多录友搞不懂 | LeetCode:104.二叉树的最大深度_哔哩哔哩_bilibili
二叉树的高度是指根节点距离叶子结点的距离,二叉树的深度是指叶子节点到根节点的距离。这两个概念看起来没什么区别,其实可以结合向量的知识来理解,这两个概念的起点和终点互换了,也就是说,计算高度的时候,以叶子结点的高度为一,而计算深度的时候,以根节点的深度为一。
说回这道题目,要求二叉树的最大深度,就要求出二叉树左右子树最大深度的最大值,这里听起来很绕,其实是一个递归的写法。
class Solution {
public:
int maxDepth(TreeNode* root) {
return getdepth(root);
}
int getdepth(TreeNode* root){
if(root == nullptr) return 0;
int leftmax = getdepth(root->left);
int rightmax = getdepth(root->right);
return 1 + max(leftmax,rightmax);
}
};
这道题目的方法其实是后序遍历的方法,求的是二叉树的最大高度,为什么要求二叉树的最大高度?1. 数值上最大高度和最大深度是相等的。 2. 每个节点需要知道左右子树的深度,逻辑上相当于从下往上遍历,即从叶子结点开始遍历。
学会了二叉树的最大深度,怎么求N叉树的最大深度?
方法上是一样的,都是后序遍历,在上面二叉树的题解可以发现,我定义了两个变量leftmax,rightmax来存左右子树的最大深度,那对于N叉树是否需要写多个变量,或者说统计一下每个节点孩子的数量,都不用。只需要遍历它的孩子数组每次记录最大值就可以了,和二叉树的逻辑基本是一致的。
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
int maxDepth(Node* root) {
return getdepth(root);
}
int getdepth(Node* root){
if(root == nullptr) return 0;
int longlength = 0;
for(auto a:root->children){
longlength = max(longlength,getdepth(a));
}
return longlength + 1;
}
};
刚刚求了二叉树的最大深度,看到这个题目,发现好像差不多,是不是把max变成min就行了?
答案是不行,因为题目要求最小深度是根节点到叶子结点的深度,如果根节点没有左孩子的情况下,最小深度为1,但这显然是不对的。
其实这题用层序遍历更好,不需要考虑那么多,这里给出层序遍历的代码。
class Solution {
public:
int minDepth(TreeNode* root) {
queue<TreeNode*> qu;
int result = 0;
if(root == nullptr)return result;
qu.push(root);
while(!qu.empty()){
int size = qu.size();
while(size--){
TreeNode* node = qu.front();
qu.pop();
if(node->left)qu.push(node->left);
if(node->right)qu.push(node->right);
if(!node->left && !node->right){
result++;
return result;
}
}
result++;
}
return result;
}
};
那我就想用后序遍历解决这道题目怎么办?也不是不行,加几个条件判断就行。一开始我想只处理根节点的特殊情况,发现不仅仅是根节点不好处理的问题,还有其他节点在递归的时候也会遇到这种特殊情况,所以必须在函数里面写判断条件。
怎么处理?什么条件?特殊情况无非两种,一种是左孩子为空,右孩子不空,一种是右孩子为空,左孩子不空,任何一个孩子是空的,那只需要取另外一边的深度加上就行。
class Solution {
public:
int minDepth(TreeNode* root) {
return getdepth(root);
}
int getdepth(TreeNode* root){
if(root == nullptr) return 0;
int leftmin = getdepth(root->left);
int rightmin = getdepth(root->right);
if(root->left != nullptr && root->right == nullptr){
return 1+leftmin;
}
if(root->left == nullptr && root->right != nullptr){
return 1+rightmin;
}
return 1+min(leftmin,rightmin);
}
};
针对所有二叉树,只需要后序遍历所有节点就行。
class Solution {
public:
int countNodes(TreeNode* root) {
return getNodes(root);
}
int getNodes(TreeNode* node){
if(node == nullptr)return 0;
int left = getNodes(node->left);
int right = getNodes(node->right);
return left+right+1;
}
};
但这是完全二叉树,还可以利用完全二叉树的特殊性质——其中一部分是满二叉树。什么意思?看视频。
怎么判断子树是满二叉树?在完全二叉树中,只要左边深度和右边深度相等那就是满二叉树,可以自行画图验证。
另外,注意length的定义从零开始,最后统计下来长度会比实际长度少一,为什么要这样做?
因为后面要用到(2<<leftlength)得到的结果是2的leftlength+1次方。
class Solution {
public:
int countNodes(TreeNode* root) {
return getNodes(root);
}
int getNodes(TreeNode* node){
if(node == nullptr)return 0;
TreeNode* left = node->left;
TreeNode* right = node->right;
int leftlength = 0;
int rightlength = 0;
while(left){
left = left->left;
leftlength++;
}
while(right){
right = right->right;
rightlength++;
}
if(leftlength==rightlength)return (2<<leftlength)-1;
int leftnum = getNodes(node->left);
int rightnum = getNodes(node->right);
return leftnum + rightnum + 1;
}
};