文章目录
104.二叉树的最大深度
文章讲解:104.二叉树的最大深度
视频讲解:二叉树的高度和深度有啥区别?究竟用什么遍历顺序?很多录友搞不懂 | 104.二叉树的最大深度
状态:最大深度明显就是指二叉树的高度,用后序遍历解题。//或者是直接层序遍历,个人感觉最简单。
首先明确为什么要后续遍历呢?
后续遍历是左右中,也就是说对于根结点而言,我们首先遍历到左子树的最深,然后遍历到右子树的最深。最后把两个子树的最深的那一颗 + 1,就是我们的最大深度了。
思路
伪代码
- 确定参数和返回值:
第一反应肯定是自带参数 cur 和 depth 来表示当前遍历的深度。当然了,我们的返回值也是返回最大深度即可。也就是说,深度和我们函数栈的深度是一致的,这样使用递归来理解也是非常简单了。
int traversal(TreeNode* cur, int depth)
- 确定终止条件:如果为空结点的话,就返回当前的深度,也就是我们需要把当前节点的深度一直向上返回上去。
if (node == nullptr) return depth;
- 确定单层递归逻辑:
先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的值,也就是说我们每次进入递归,都把 depth + 1 即可。
if (cur == nullptr) return depth;
int maxLeft = traversal(cur->left, depth + 1);
int maxRight = traversal(cur->right, depth + 1);
return max(maxLeft, maxRight);
CPP代码
class Solution {
public:
int traversal(TreeNode* cur, int depth) {
if (cur == nullptr) return depth;
int maxLeft = traversal(cur->left, depth + 1);
int maxRight = traversal(cur->right, depth + 1);
return max(maxLeft, maxRight);
}
int maxDepth(TreeNode* root) {
int depth = 0;
return traversal(root, depth);
}
};
111.二叉树的最小深度
文章讲解:111.二叉树的最小深度
视频讲解:看起来好像做过,一写就错! | LeetCode:111.二叉树的最小深度
状态:本题仍然需要收集子结点的信息回传给根结点,显然使用后序遍历。但是第一次做题完美落入陷阱
思路
本题和上一题思想很类似,但是有一个细节需要我们讨论。
使用递归遍历的话,再确定单层递归逻辑很容易写成:
int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
int result = 1 + min(leftDepth, rightDepth);
return result;
这个代码就出现了错误!如下图所示:
显而易见,我们一定要处理其中一个子结点为空的情况!
伪代码
- 确定参数和返回值
int getdepth(TreeNode* node){
}
- 确定终止条件
if (node == nullptr) return 0;
- 确定单层递归逻辑:
int leftdepth = getdepth(node->left);
int rightdepth = getdepth(node->right);
//当一个左子树为空,右不为空,这时并不是最低点
if (node->left == NULL && node->right != NULL)
return 1 + rightdepth;
//当一个右子树为空,左不为空,这时并不是最低点
if (node->right == NULL && node->left != NULL)
return 1 + leftdepth;
return 1 + min (lethdepth + rightdepth);
CPP代码
class Solution {
public:
int getDepth(TreeNode* node) {
if (node == NULL) return 0;
int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
// 中
// 当一个左子树为空,右不为空,这时并不是最低点
if (node->left == NULL && node->right != NULL) {
return 1 + rightDepth;
}
// 当一个右子树为空,左不为空,这时并不是最低点
if (node->left != NULL && node->right == NULL) {
return 1 + leftDepth;
}
int result = 1 + min(leftDepth, rightDepth);
return result;
}
int minDepth(TreeNode* root) {
return getDepth(root);
}
};
222.完全二叉树的节点个数
文章讲解:222.完全二叉树的节点个数
视频讲解:要理解普通二叉树和完全二叉树的区别! | LeetCode:222.完全二叉树节点的数量
状态:在不利用完全二叉树特性的情况下:迭代法肯定用层序遍历,递归法的话还是后序遍历。完全二叉树的特性如何利用起来,做的时候确实没想到,就是需要借助于满二叉树的特点,左右遍历看左右子树是不是满二叉树。
思路
首先要确定好完全二叉树的特性:
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置,即最后一层是按照层序编号。
视为普通二叉树-递归
这个甚至没啥好说的,该咋做就咋做
视为普通二叉树-迭代
其实很多题目的迭代法都是用层序遍历来进行的。还记得层序遍历的迭代法吗?直接把那套代码一改就完事儿了。
利用完全二叉树特性-递归
说到完全二叉树就不得不提一嘴满二叉树。因为完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
对于情况一,可以直接用 2^树深度 - 1 来计算,注意这里根节点深度为1。
对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,甚至我们可以将叶子结点都可以视为结点数量为1的满二叉树,然后依然可以按照情况1来计算。
那么问题的关键转换成了如何去判断一个左子树或者右子树是不是满二叉树呢?
答案就是在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树
|
|
那么其中的关键代码如何写呢?请看利用完全二叉树特性-递归
伪代码
视为普通二叉树-递归伪代码
- 确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。
int getNodesNum(TreeNode* cur)
- 确定终止条件:如果为空节点的话,就返回0,表示节点数为0。
if (cur == NULL) return 0;
- 确定单层递归的逻辑:先求它的左子树的节点数量,再求右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量。
int leftNum = getNodesNum(cur->left); // 左
int rightNum = getNodesNum(cur->right); // 右
int treeNum = leftNum + rightNum + 1; // 中
return treeNum;
视为普通二叉树-迭代伪代码
queue<TreeNode*> que;//一定要注意这里放入的是结点
int result = 0;
if (root) que.push(root);
while (!st.empty()){
int size = que.size(); //记录本层应该弹出的元素
vector<int> vec; //用来临时存放该层的结果
for (int i = 0; i < size; i++){
TreeNode* node = que.front;
que,pop();
result++; //记录结点数量
if(node->left) que.push(node->left); // 空结点不入队
if(node->right) que.push(node->right); //空结点不如队
}
}
return result;
利用完全二叉树特性-递归伪代码
- 在递归三部曲中,第二部:终止条件的写法应该是这样的:
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,返回满足满二叉树的子树节点数量
}
- 第三部,单层递归的逻辑:(可以看出使用后序遍历)
int leftTreeNum = countNodes(root->left); // 左
int rightTreeNum = countNodes(root->right); // 右
int result = leftTreeNum + rightTreeNum + 1; // 中
return result;
- 整个单层递归逻辑可以精简成
return countNodes(root->left) + countNodes(root->right) + 1;
CPP代码
视为普通二叉树-递归
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);
}
};
//精简之后的CPP代码
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == NULL) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
视为普通二叉树-迭代
class Solution {
public:
int countNodes(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
int result = 0;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
result++; // 记录节点数量
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
利用完全二叉树特性-递归
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;
}
};