一、二叉树的最大深度
1.题目
104. 二叉树的最大深度 - 力扣(LeetCode)
2.思路
2.1递归法
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
根节点的高度就是二叉树的最大深度,所以本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
为什么前序求深度?
前序(中左右):中就是更新当前遍历的深度。然后求左子树的深度和右字数的深度。这种遍历方法有个回溯过程。
为什么后序可以求高度?
后序(左右中):先求左子树的高度,再求右子树的高度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
整体代码如下:
2.2迭代法(最好理解的方法)
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数。
class Solution {
public:
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
int depth = 0;
if(root != NULL) que.push(root);
while(!que.empty()){
depth += 1; //到每一层,深度加一
int size = que.size();
for(int i=0;i<size;i++){
TreeNode* temp = que.front();
que.pop();
if(temp->left) que.push(temp->left);
if(temp->right) que.push(temp->right);
}
}
return depth;
}
};
二、二叉树的最小深度
1.题目
111. 二叉树的最小深度 - 力扣(LeetCode)
2.思路
这道题使用层序遍历法。最小深度,即找到深度最小的叶子节点(没有左右子树)。
class Solution {
public:
int minDepth(TreeNode* root) {
queue<TreeNode*> que;
int depth = 0;
if(root != NULL) que.push(root);
while(!que.empty()){
depth += 1;
int size = que.size();
for(int i=0;i<size;i++){
TreeNode* temp = que.front();
que.pop();
if(temp->left) que.push(temp->left);
if(temp->right) que.push(temp->right);
if(!(temp->left) && !(temp->right)) return depth;//从上到下直接找到子叶就行
}
}
return depth;
}
};
三、完全二叉树的节点个数
1.题目
222. 完全二叉树的节点个数 - 力扣(LeetCode)
2.思路
层序遍历法:把每一层的元素加起来即可。
容易出错的点:for(int i = 0;i < size;i++) 不能写成for(int i = 0;i < que.size();i++),因为在每一次遍历的时候都会重新计算que.size()。
class Solution {
public:
int countNodes(TreeNode* root) {
queue<TreeNode*> que;
int num = 0;
if(root) {que.push(root);}
else{return num;}
while(!que.empty()){
int size = que.size();
num += que.size();
for(int i = 0;i < size;i++){
TreeNode* node = que.front();
que.pop();
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
}
return num;
}
};
四、平衡二叉树
1.题目
110. 平衡二叉树 - 力扣(LeetCode)
2.思路
递归法
既然要求比较高度,必然是要后序遍历。
递归三步曲分析:
1.明确递归函数的参数和返回值
参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。
2.明确终止条件
3.明确单层递归的逻辑
那么如何标记左右子树是否差值大于1呢?
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
总体代码如下:
class Solution {
public:
//求高度,后序遍历:左右中
int getheight(TreeNode* node){
if(node == NULL) return 0;
int leftheiht = getheight(node->left);
if(leftheiht == -1) return -1;//左子树已经不是平衡二叉树
int rightheight = getheight(node->right);
if(rightheight == -1) return -1;//右子树已经不是平衡二叉树
if(abs(leftheiht-rightheight)>1)//中
return -1;
else{
return 1+max(leftheiht,rightheight);
}
}
bool isBalanced(TreeNode* root) {
return getheight(root) == -1 ? false :true;
}
};
五、二叉树的所有路径
1.题目
257. 二叉树的所有路径 - 力扣(LeetCode)
2.思路
递归法
1.递归函数参数以及返回值
要传入根节点,记录每一条路径的path,和存放结果集的result,这里递归不需要返回值,代码如下:
2.确定递归终止条件
本题要找到叶子节点,就开始结束的处理逻辑了(把路径放进result里)。
那么什么时候算是找到了叶子节点? 是当 cur不为空,其左右孩子都为空的时候,就找到叶子节点。
为什么没有判断cur是否为空呢,因为下面的逻辑可以控制空节点不入循环。
再来看一下终止处理的逻辑。
这里使用vector 结构path来记录路径,所以要把vector 结构的path转为string格式,再把这个string 放进 result里。
那么为什么使用了vector 结构来记录路径呢? 因为在下面处理单层递归逻辑的时候,要做回溯,使用vector方便来做回溯。
3.确定单层递归逻辑
因为是前序遍历,需要先处理中间节点,中间节点就是我们要记录路径上的节点,先放进path中。
然后是递归和回溯的过程,上面说过没有判断cur是否为空,那么在这里递归的时候,如果为空就不进行下一层递归了。所以递归前要加上判断语句,下面要递归的节点是否为空,如下
整体代码:
class Solution {
private:
void traversal(TreeNode* cur,vector<int>& path,vector<string>& result){
path.push_back(cur->val);//中节点处理,因为最后一个节点也要加入到path中
//叶子节点处理
if(cur->left == NULL && cur->right == NULL){
string str;
for(int i =0;i<path.size()-1;i++){
str += to_string(path[i]);
str += "->";
}
str += to_string(path[path.size()-1]);
result.push_back(str);
return;
}
//左
if(cur->left){
traversal(cur->left,path,result);
path.pop_back();//回溯
}
if(cur->right){
traversal(cur->right,path,result);
path.pop_back();//回溯
}
}
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<int> path;
vector<string> result;
if(root == NULL) return result;
traversal(root,path,result);
return result;
}
};