二叉树的最大深度
递归法
- 二叉树节点的深度:从根节点到该节点的最长简单路径边的条数或节点的个数。
- 二叉树节点的高度:从该节点到叶子节点的最长简单路径边的条数或节点的个数。
使用前序遍历获得深度,使用后序遍历获得高度。高度是需要从树底部向上遍历,这正好是后序的顺序,我们将左右子树的信息最后传给中间节点做处理(+1),就得到了每个节点的高度。
这道题是求最大深度,本来是应该按前序遍历来求,但最简单的方法却是后序遍历。因为最大深度就是根节点的高度,后序遍历求根节点高度也是合理的。
- 确定递归函数的参数和返回值:参数就是传入的树的根节点,返回的就以此节点为根节点的树的深度。
- 确定返回条件:如果传入节点为空,深度应为0.
- 单层递归逻辑:求左子树的深度,求右子树的深度,返回两者最大值加1.
class Solution{
public:
int getDepth(TreeNode* node){
if(node == NULL) return 0;
int leftdepth = getDepth(node->left);
int rightdepth = getDepth(node->right);
int depth = 1 + max(leftdepth, rightdepth);
return depth;
}
int maxDepth(TreeNode* root){
return getDepth(root);
}
};
class Solution{
public:
int result;
void getDepth(TreeNode* node, int depth){
if(!node) return;
result = depth > result ? depth : result; // 记录当前非空节点的深度
if(node->left) getDepth(node->left, depth + 1); // 将depth在形式参数位置+1,避免显式的回退,是很多递归法都会用到的技巧
if(node->right) getDepth(node->right, depth + 1);
}
int maxDepth(TreeNode* root){
result = 0;
getDepth(root, 1);
return result;
}
};
二叉树的最小深度
这道题我们只需注意一点,最小深度是指到叶子节点的最小距离,而叶子节点是左右节点均为空的节点。因此在后序遍历中我们不能简单地将上一题中的取最大值变为取最小值,那样会使最小值可能在没有左子树或右子树的节点处出现,但该节点不是叶子节点。最大深度中不用判断是因为最大的深度一定出现在有子树的一边。
后序遍历:
class Solution{
public:
int minDepth(TreeNode* root){
if(!root) return 0;
int leftdepth = minDepth(root->left);
int rightdepth = minDepth(root->right);
if(!root->left && root->right){ // 左子树为空,右子树不空,最小深度来自右子树
return 1 + rightdepth;
}
else if(root->left && !root->right){
return 1 + leftdepth;
}
else{ // 左子树和右子树均不为空,那么最小深度应该来自于两者的最小值
return 1 + min(leftdepth, rightdepth);
}
}
};
前序遍历:也是要注意在确定叶子节点时再比较算最小深度
class Solution{
public:
int result;
void getDepth(TreeNode* node, int depth){
if(!node) return;
// 判断是叶子节点再判断是否是最小深度
if(!node->left && !node->right){
result = depth < result ? depth : result;
}
getDepth(node->left, depth + 1);
getDepth(node->right, depth + 1);
}
int minDepth(TreeNode* root){
if(!root) return 0;
result = INT_MAX;
getDepth(root, 1);
return result;
}
};
完全二叉树的节点个数
可以按照普通二叉树来处理,这样时间复杂度为 O(n)。用后序、层序都是可以的。
class Solution{
public:
int countNodes(TreeNode* root){
if(!root) return 0;
int leftnum = countNodes(root->left);
int rightnum = countNodes(root->right);
return 1 + leftnum + rightnum;
}
};
但我们可以使用完全二叉树的性质,我们知道完全二叉树只有最后一层可能是不满的,且最后一层的节点集中在树的左侧。
因此,完全二叉树只有两种情况:
- 本身就是一个满二叉树,直接可以用 2^树深度-1 来得到节点数目。
- 本身不满,但递归左节点和右节点,一定可以在某一深度递归得到满二叉树,就变成上一种情况来处理了。
那么如何判断一个完全二叉树是满的呢?只需要判断左侧深度与右侧深度是否相等。
class Solution{
public:
int countNodes(TreeNode* root){
if(!root) return 0;
int leftdepth = 0, rightdepth = 0;
TreeNode* left = root->left;
TreeNode* right = root->right;
while(left){
left = left->left; // 遍历左侧深度
leftdepth++;
}
while(right){
right = right->right;
rightdepth++;
}
if(leftdepth == rightdepth){ // 如果左侧深度和右侧深度相等,说明遇到了满二叉树,可以直接计算节点数目了
return (2 << leftdepth) - 1;
}
// 不然还是一个完全树,继续向下找,左右子树节点数目+1
return 1 + countNodes(root->left) + countNodes(root->right);
}
};