知识点 树
描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度,根节点的深度视为 1 。
数据范围:节点的数量满足 0≤n≤100 ,节点上的值满足0≤val≤100
进阶:空间复杂度 O(1) ,时间复杂度O(n)
假如输入的用例为{1,2,3,4,5,#,6,#,#,7},那么如下图:
示例1
输入:{1,2,3,4,5,#,6,#,#,7}
返回值:4
示例2
输入:{}
返回值:0
方法一:递归(推荐使用)
知识点:二叉树递归
递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能将原本的问题分解为更小的子问题,这是使用递归的关键。
而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。
思路:
最大深度是所有叶子节点的深度的最大值,深度是指树的根节点到任一叶子节点路径上节点的数量,因此从根节点每次往下一层深度就会加1。因此二叉树的深度就等于根节点这个1层加上左子树和右子树深度的最大值,即rootdepth=max(leftdepth,rightdepth)+1。而每个子树我们都可以看成一个根节点,继续用上述方法求的深度,于是我们可以对这个问题划为子问题,利用递归来解决:
终止条件: 当进入叶子节点后,再进入子节点,即为空,没有深度可言,返回0.
返回值: 每一级按照上述公式,返回两边子树深度的最大值加上本级的深度,即加1.
本级任务: 每一级的任务就是进入左右子树,求左右子树的深度。
具体做法:
step 1:对于每个节点,若是不为空才能累计一次深度,若是为空,返回深度为0.
step 2:递归分别计算左子树与右子树的深度。
step 3:当前深度为两个子树深度较大值再加1。
图示:
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
#include <cstddef>
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
//如果是空节点,则没有深度
if(pRoot == NULL)
return 0;
//返回子树深度+1
return max(TreeDepth(pRoot->left),TreeDepth(pRoot->right)) + 1;
}
};
复杂度分析:
时间复杂度:O(n),其中n为二叉树的节点数,遍历整棵二叉树
空间复杂度:O(n),最坏情况下,二叉树化为链表,递归栈深度最大为n
方法二:层次遍历(扩展思路)
知识点:队列
队列是一种仅支持在表尾进行插入操作、在表头进行删除操作的线性表,插入端称为队尾,删除端称为队首,因整体类似排队的队伍而得名。它满足先进先出的性质,元素入队即将新元素加在队列的尾,元素出队即将队首元素取出,它后一个作为新的首。
思路:
既然是统计二叉树的最大深度,除了根据路径到达从根节点到达最远的叶子节点以外,我们还可以分层统计。对于一棵二叉树而言,必然是一层一层的,那一层就是一个深度,有的层可能会很多节点,有的层如根节点或者最远的叶子节点,只有一个节点,但是不管多少个节点,它们都是一层。因此我们可以使用层次遍历,二叉树的层次遍历就是从上到下按层遍历,每层从左到右,我们只要每层统计层数即是深度。
具体做法:
step 1:既然是层次遍历,我们遍历完一层要怎么进入下一层,可以用队列记录这一层中节点的子节点。队列类似栈,只不过是一个先进先出的数据结构,可以理解为我们平时的食堂打饭的排队。因为每层都是按照从左到右开始访问的,那自然记录的子节点也是从左到右,那我们从队列出来的时候也是从左到右,完美契合。
step 2:在刚刚进入某一层的时候,队列中的元素个数就是当前层的节点数。比如第一层,根节点先入队,队列中只有一个节点,对应第一层只有一个节点,第一层访问结束后,它的子节点刚好都加入了队列,此时队列中的元素个数就是下一层的节点数。因此遍历的时候,每层开始统计该层个数,然后遍历相应节点数,精准进入下一层。
step 3:遍历完一层就可以节点深度就可以加1,直到遍历结束,即可得到最大深度。
图示:
class Solution {
public:
int maxDepth(TreeNode* root) {
//空节点没有深度
if(root == NULL)
return 0;
//队列维护层次后续节点
queue<TreeNode*> q;
//根入队
q.push(root);
//记录深度
int res = 0;
//层次遍历
while(!q.empty()){
//记录当前层有多少节点
int n = q.size();
//遍历完这一层,再进入下一层
for(int i = 0; i < n; i++){
TreeNode* node = q.front();
q.pop();
//添加下一层的左右节点
if(node->left)
q.push(node->left);
if(node->right)
q.push(node->right);
}
//深度加1
res++;
}
return res;
}
};
复杂度分析:
时间复杂度:O(n),其中n为二叉树的节点数,遍历整棵二叉树
空间复杂度:O(n),辅助队列的空间最坏为n