给定一个二叉树 root,返回其最大深度。二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。二叉树相关的题解,一般都有深度优先遍历和广度优先遍历两种解法,在深度优先遍历中,可能存在前序、中序和后序解,前中后序又存在递归解和迭代解,解法丰富,思路很广,看似简单的题目实则“复杂”。
class Solution { // 后序递归遍历,详细
public:
int getdepth(TreeNode* node) {
if (node == nullptr) 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 maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
};
上述代码是深度优先遍历中后序遍历的递归解法,分为详细版和精简版。1. 确定迭代函数的参数,这里只需要一个一个树节点即可;2. 确定终止条件,当节点为空时,递归结束;3. 确定基本逻辑,这里我们使用后序遍历,左右根的顺序,先充分探索当前节点的左子树,然后探索右子树,将左右子树的深度返回给当前节点,一层一层的将信息返回至根节点。(不可以是前序和中序,前序先计算当前节点的深度,没法到达下一层节点,中序亦然如此)根节点得到左子树和右子树的深度,将最大的深度加1作为返回值。
class Solution { // 前序递归遍历
public:
int result;
void getdepth(TreeNode* node, int depth) {
result = result > depth ? result : depth
if (node->left == nullptr && node->right nullptr) return ;
if (node->left) {
depth++;
getdepth(node->left, depth);
depth--;
}
if (node->right) {
depth++;
getdepth(node->right, depth);
depth--;
}
return ;
}
int maxDepth(TreeNode* root) {
result = 0;
if (root == nullptr) return result;
getdepth(root, 1);
return result;
}
};
上述代码为前序遍历的递归解法,depth 负责跟踪当前层的深度,result 接收最大深度。每进入一层,depth 就要自加,每回溯一层,depth 就要自减,depth 是一个动态的变量,跟随当前递归执行在二叉树的哪一层,如果 depth 不自减,那么就无法确定当前的层数。为什么说这个是前序遍历呢,因为我们是先用 result 确定当前的深度,然后才遍历左子树和右子树,所以是中左右的顺序。还可以使用层序遍历解,很简单,在此不介绍。
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明:叶子节点是指没有子节点的节点。这一题乍一看和104.二叉树的最大深度相同,只需要修改max函数为min函数即可,实则不然。假设一个节点没有左子树,但是有右子树,这时候min函数取0,将当前节点作为叶子节点,这是不对的,需要加一些限制条件。
class Solution { // 后序递归遍历,详细
public:
int getdepth(TreeNode* node) {
if (node == nullptr) return 0;
int leftdepth = getdepth(node->left);
int rightdepth = getdepth(node->right);
if (node->left == nullptr && node->right != nullptr) return 1 + rightdepth;
if (node->right == nullptr && node->left != nullptr) return 1 + leftdepth;
int depth = 1 + min(leftdepth, rightdepth);
return depth;
}
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
return getdepth(root);
}
};
class Solution { // 后序遍历递归,精简
public:
int minDepth(TreeNode* root) {
if (root == NULL) return 0;
if (root->left == NULL && root->right != NULL) {
return 1 + minDepth(root->right);
}
if (root->left != NULL && root->right == NULL) {
return 1 + minDepth(root->left);
}
return 1 + min(minDepth(root->left), minDepth(root->right));
}
};
上面代码是后序遍历递归解法,我们加入了一些判定条件。在当前节点没有左子树,有右子树的时候,返回1+右子树的深度;在当前节点没有右子树,有左子树的时候,返回1+左子树的深度;在当前节点有左右子树的时候,返回1+左右子树最小的深度。
和二叉树的最大、最小深度类似,应该也可以使用递归法,挨个遍历每个节点。这里我们提供一个更简单的解法,就是上文提到的层序遍历解。
class Solution { // 层序遍历
public:
int maxDepth(Node* root) {
queue que;
if (root != nullptr) que.push(root);
int layer = 0;
while (!que.empty()) {
int size = que.size();
while (size--) {
Node* node = que.front();
que.pop();
for (int i = 0; i < node->children.size(); i++) {
if (node->children[i]) que.push(node->children[i]);
}
}
++layer;
}
return layer;
}
};
层序遍历是一个模板,使用一个队列进队和出队遍历整个树,在这个过程中,可以对树进行一些操作,很好用,很无敌,不能常用,不然会对二叉树陌生。
给你一颗完全二叉树的根节点 root,求出该树的节点个数。完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该该层包含 1~2^h 个节点。本题有两种思路,一种是将完全二叉树视为普通二叉树,使用普通二叉树的遍历思路解决;另一种则是根据完全二叉树的性质制定解法。两种思路都可以,后者时间复杂度低于前者。
class Solution { // 后序递归遍历,详细
private:
int getNodesNum(TreeNode* cur) {
if (cur == NULL) return 0;
int leftNum = getNodesNum(cur->left); // 左
int rightNum = getNodesNum(cur->right); // 右
int treeNum = leftNum + rightNum + 1; // 中
return treeNum;
}
public:
int countNodes(TreeNode* root) {
return getNodesNum(root);
}
};
class Solution { // 后序递归遍历,精简
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
class Solution { // 层序遍历
public:
int countNodes(TreeNode* root) {
queue que;
if (root != nullptr) que.push(root);
int count = 0;
while(!que.empty()) {
int size = que.size();
count += size;
while (size--) {
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right) ;
}
}
return count;
}
};
上面代码为普通二叉树的思路,给出了后序遍历和层序遍历的解法。
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
while (left) { // 求左子树深度
left = left->left;
leftDepth++;
}
while (right) { // 求右子树深度
right = right->right;
rightDepth++;
}
if (leftDepth == rightDepth) {
return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0
}
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
上面代码为完全二叉树的解法,完全二叉树,有两种情况,一种是满二叉树,另一种不满二叉树,当满二叉树的时候,可以直接用公式计算出节点个数;在当前节点不是满二叉树的时候,可以遍历孩子节点,找到满足二叉树的节点,然后通过公式计算出节点个数。如何判断是不是为满二叉树呢?只要遍历当前节点的左子树和右子树,只要其深度相同,那就是满二叉树,这个条件满足的前提是该树为完全二叉树。