104. 二叉树的最大深度
首先需要明白最大深度是什么,之前的题也刷过,不过是用层序法做的。最大深度等于根节点的高度,深度是从上往下算的,高度是从下往上算的。这里使用递归的后序遍历(为什么呢,暂时还没有自己的方法论,先跟着卡哥的思路写),从叶子结点开始计算节点的高度,直到算到根节点时的高度即为最大深度。
下面是递归三部曲:
- 输入传入的是结点,输出是深度
int
- 终止条件是结点为
NULL
时,此时结点时没有高度的,返回0 - 递归的单层逻辑,还是左右中的处理顺序,这里说一下最后返回的时候是返回上一层结点,所以返回值为1+当前结点的高度。同时因为是要找二叉树的最大深度,返回的应该是同一层结点中较大的那个结点的高度。
例如当left为9,right为20时,9的高度为1,20的高度为2,所以这一层返回的应该是2 + 1。
代码如下:
int maxDepth(TreeNode* root) {
if(root == NULL) return 0;
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return 1 + max(left,right);
}
当然,迭代中的层序遍历完美符合寻找深度,也符合正常的思维,但是递归法还是需要掌握,只能说先积累经验吧。
559. N 叉树的最大深度
N叉树也是一样的,只不过在处理结点的时候套用一个循环来计算高度。
代码如下:
int maxdepth(node* root) {
if (root == 0) return 0;
int depth = 0;
for (int i = 0; i < root->children.size(); i++) {
depth = max (depth, maxdepth(root->children[i]));
}
return depth + 1;
}
先使用一个depth
来记录这一层结点的高度,再依次进行对比找出最大的高度,最后返回的时候+1。
111. 二叉树的最小深度
本题使用迭代的层序遍历也是很简单,并且可以忽略根节点下左右子树其中一个为空的情况。这里使用递归的后序遍历。上一题最后返回的是max(leftdep , rightdep)
,那么本题返回的应该是min(leftdep , rightdep)
,但是需要考虑刚刚说的节点下左右子树其中一个为空的情况,否则不管另一个不为空的孩子下面有多少结点,最后返回的答案为1,也就是根节点的高度。
比如说这个就是左子树为空的情况。(力扣上的二叉树可视化功能很好用,方便自测)
递归三部曲:
- 输入是结点,输出是深度
int
- 终止条件这里需要添加上述的特殊情况,正常情况下为
root == NULL
- 单层递归逻辑和上一题差不多,属于后序遍历
代码如下:
int minDepth(TreeNode* root) {
if(root == NULL) return 0;
//首先记录当前结点的高度,以便作为特殊情况的返回值
int left = minDepth(root->left);
int right = minDepth(root->right);
//两种特殊情况
if(root->left == NULL && root->right != NULL){
return 1 + right;
}
if(root->left != NULL && root->right == NULL){
return 1 + left;
}
//正常情况下的返回值
return 1 + min(left,right);
}
222.完全二叉树的节点个数
本题使用常规的遍历模板,再使用一个计数,遍历后返回结果就可以得出结点个数。
考虑到题目给出的是完全二叉树,所以使用完全二叉树的特性来做,并且使用递归法。
这里使用完全二叉树的特性,只有最底层结点没有填满外,以上的结点都是满的;所以只要判断子树是否为完全二叉树,并知道其深度,就能计算出结点个数。如果一棵树的左下角的结点和右下角结点的深度相等,那么他就是一颗完全二叉树。这样如果一棵子树为完全二叉树的话,只用遍历其底层最外侧的左右结点就可以计算出结点个数。
递归三部曲:
- 输入是结点,输出是结点及其子树的个数
int
- 终止条件:结点为空、判断一棵树是否为完全二叉树
- 单层递归逻辑:如果子树不是完全二叉树,那么进入单层递归逻辑。我发现这种返回值为
int
需要求深度高度个数的递归,都是后序。先把左右孩子递归结束,再分别用两个变量记录结果,最后return
的时候根据题意处理。
代码如下:
int countNodes(TreeNode* root) {
if(root == NULL) return 0;
//通过外侧结点来判断子树是否是完全二叉树
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftdep = 0;
int rightdep = 0;
while(left){
left = left->left;
leftdep++;
}
while(right){
right = right->right;
rightdep++;
}
if(leftdep == rightdep) return (2<<leftdep) - 1;
int leftnum = countNodes(root->left); //这里是root的左右孩子,上面的root->left不是用来递归遍历的
int rightnum = countNodes(root->right);
return leftnum + rightnum + 1;
}
这里2<<leftdep
的移位运算符表示2的leftdep次方。
写给自己的总结(不知道对不对)
最近做的递归,返回类型都是int
,也就是题目需要求的是什么的个数(一般是后序),这样一个递归函数就可以直接搞定,那么这种情况下递归函数最后return
的值递归到最后也就是需要返回的答案,因为一层的递归需要统计的个数是在上一层的基础上计算的。这样这类题目(还没总结出来是哪类)的递归函数的写法就是:
- 首先确定终止条件有没有特殊情况,如111和222
- 然后就是对左右孩子的递归
- 最后对递归出来的左右孩子的结果处理
感觉基本就是一个后序的过程。
这里就是自己的刷题感悟,希望有大佬指正!