110.平衡二叉树 (优先掌握递归)
高度与深度
二叉树的高度与深度:
- 高度:只能从下到上去查,所以只能后序遍历(左右中)
- 深度:求深度可以从上到下去查 所以需要前序遍历(中左右)
104.二叉树的最大深度
该题使用后序遍历,因为此时代码就是在求根节点的高度。
只不过,根节点的高度就等于最大深度。
递归法
平衡二叉树,需要求左右孩子的高度,因此函数返回值是高度。
每个节点的左右子树的高度相差都不超过1,因此遍历所有节点都需要满足条件。
而判断是否满足平衡二叉树的条件:返回 -1。一旦有节点不满足条件,就可以直接返回。
class Solution {
public boolean isBalanced(TreeNode root) {
return getHeight(root) > -1 ? true : false;
}
private int getHeight(TreeNode root){ // 返回节点的高度,若左右高度相差超过1,就返回-1
// 边缘条件
if(root==null) return 0;
// if(root.right==null && root.left==null) return 1;
// 递归逻辑
int leftHeight = getHeight(root.left);
if(leftHeight==-1) return -1;
int rightHeight = getHeight(root.right);
if(rightHeight==-1) return -1;
return Math.abs(leftHeight - rightHeight) > 1 ? -1: Math.max(leftHeight, rightHeight) + 1;
}
}
迭代法
可以使用层序遍历来求深度,但是就不能直接用层序遍历来求高度。
本题的迭代方式可以先定义一个函数,专门用来求高度。
这个函数通过栈模拟的后序遍历,找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度)
然后再用栈来模拟后序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合
用迭代法,其实效率很低,因为没有很好的模拟回溯的过程,所以迭代法有很多重复的计算。
257. 二叉树的所有路径 (优先掌握递归)
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
return getPaths(root);
}
private List<String> getPaths(TreeNode root){
List<String> res = new ArrayList<>();
if(root==null) return res;
if(root.left==null && root.right==null){
res.add(String.valueOf(root.val));
return res;
}
List<String> leftRes = getPaths(root.left);
List<String> rightRes = getPaths(root.right);
for(int i=0; i<leftRes.size(); i++){
res.add(String.valueOf(root.val) + "->" + leftRes.get(i));
}
for(int i=0; i<rightRes.size(); i++){
res.add(String.valueOf(root.val) + "->" + rightRes.get(i));
}
return res;
}
}
代码随想录:回溯
- 终止条件:从
cur==null改为cur.left==null && cur.right==null。因为本题要找到叶子节点,就开始结束的处理逻辑了。 - 回溯法的参数与返回值:要传入根节点,记录每一条路径的path,和存放结果集的result,这里递归不需要返回值。
正因此,不需要频繁创建List对象。 - 单层递归逻辑:会先判空。(因为终止条件不会判空)
- 回溯:回溯与递归一一对应。每次递归时,都应该回溯。
- 注意精简版:隐藏了回溯,需要仔细琢磨。
// 完整版
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 sPath;
for (int i = 0; i < path.size() - 1; i++) {
sPath += to_string(path[i]);
sPath += "->";
}
sPath += to_string(path[path.size() - 1]);
result.push_back(sPath);
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<string> result;
vector<int> path;
if (root == NULL) return result;
traversal(root, path, result);
return result;
}
};
// 精简版
class Solution {
private:
void traversal(TreeNode* cur, string path, vector<string>& result) {
path += to_string(cur->val); // 中
// 叶子节点
if (cur->left == NULL && cur->right == NULL) {
result.push_back(path);
return;
}
if (cur->left) traversal(cur->left, path + "->", result); // 左
if (cur->right) traversal(cur->right, path + "->", result); // 右
}
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
string path;
if (root == NULL) return result;
traversal(root, path, result);
return result;
}
};
404.左叶子之和 (优先掌握递归)
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
return getLeftSum(root);
}
private int getLeftSum(TreeNode root){
// 空节点
if(root==null) return 0;
// 左孩子是叶子节点
if(root.left!=null && root.left.left==null && root.left.right==null) return root.left.val + getLeftSum(root.right);
else return getLeftSum(root.left) + getLeftSum(root.right);
}
}
代码随想录版本:按照模板来,有条理
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) return 0;
int leftValue = sumOfLeftLeaves(root.left); // 左
int rightValue = sumOfLeftLeaves(root.right); // 右
int midValue = 0;
if (root.left != null && root.left.left == null && root.left.right == null) {
midValue = root.left.val;
}
int sum = midValue + leftValue + rightValue; // 中
return sum;
}
}
222.完全二叉树的节点个数(重要:完全二叉树的性质、满二叉树的判断)
普通二叉树
class Solution {
public int countNodes(TreeNode root) {
return getNums(root);
}
private int getNums(TreeNode root){
if(root==null) return 0;
return getNums(root.left) + getNums(root.right) + 1;
}
}
完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
完全二叉树只有两种情况:
- 满二叉树:可以直接用
2^树深度 - 1来计算,注意这里根节点深度为1。 - 最后一层叶子节点没有满:分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。

如何去判断一个左子树或者右子树是不是满二叉树呢?
在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树。
如果不是完全二叉树,那就不可用。

代码随想录解法:
class Solution {
/**
* 针对完全二叉树的解法
*
* 满二叉树的结点数为:2^depth - 1
*/
public int countNodes(TreeNode root) {
if (root == null) return 0;
TreeNode left = root.left;
TreeNode right = root.right;
int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
while (left != null) { // 求左子树深度
left = left.left;
leftDepth++;
}
while (right != null) { // 求右子树深度
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;
}
}

被折叠的 条评论
为什么被折叠?



