第六章 二叉树part03
2026.03.08 02.11 第十五天
110 平衡二叉树
既然要求比较高度,必然是要后序遍历。
递归三步曲分析:
1.明确递归函数的参数和返回值
参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。
那么如何标记左右子树是否差值大于1呢?
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
2.明确终止条件
递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0
3.明确单层递归的逻辑
如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。
本题每个节点都要进行判断,因为每个节点都要判断两个子树的高度是否符合要求,因此使用统一的递归条件即可。
递归法:
class Solution {
public:
int getDepth(TreeNode* node) {
if(node == nullptr) {
return 0;
}
int leftDepth = getDepth(node->left);
if(leftDepth == -1) return -1;
int rightDepth = getDepth(node->right);
if(rightDepth == -1) return -1;
return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth);
}
bool isBalanced(TreeNode* root) {
return getDepth(root) == -1 ? false : true;
}
};
迭代法实现:
迭代方式可以先定义一个函数,专门用来求高度。
这个函数通过栈模拟的后序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度)
然后再用栈来模拟后序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合
class Solution {
private:
int getDepth(TreeNode* cur) {
stack<TreeNode*> st;
if (cur != NULL) st.push(cur);
int depth = 0; // 记录深度
int result = 0;
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
st.push(node); // 中
st.push(NULL);
depth++;
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
} else {
st.pop();
node = st.top();
st.pop();
depth--;
}
result = result > depth ? result : depth;
}
return result;
}
public:
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return true;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
if (abs(getDepth(node->left) - getDepth(node->right)) > 1) {
return false;
}
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return true;
}
};
257 二叉树的所有路径
题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。
在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。
递归法:
class Solution {
public:
void tarvelsal(TreeNode* node, vector<int>& path, vector<string>& result) {
path.push_back(node->val);
if(node->left == nullptr && node->right == nullptr) {
string spath;
for(int i = 0; i < path.size() - 1; i++) {
spath += to_string(path[i]) + "->";
}
spath += to_string(path[path.size() - 1]);
result.push_back(spath);
return;
}
if(node->left) {
tarvelsal(node->left, path, result);
path.pop_back();
}
if(node->right) {
tarvelsal(node->right, path, result);
path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
vector<int> path;
if(root == nullptr) return result;
tarvelsal(root, path, result);
return result;
}
};
进一步可以:
在第二版本的代码中,其实仅仅是回溯了 -> 部分(调用两次pop_back,一个pop> 一次pop-),大家应该疑惑那么 path += to_string(cur->val); 这一步为什么没有回溯呢? 一条路径能持续加节点 不做回溯吗?
其实关键还在于 参数,使用的是 string path,这里并没有加上引用& ,即本层递归中,path + 该节点数值,但该层递归结束,上一层path的数值并不会受到任何影响。
也就是使用实参和形参的特性,实现path的回溯,很巧妙。
迭代法:
迭代法需要使用栈来模拟递归过程。
404 左叶子之和
判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。
如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子
递归法:
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if(root == nullptr) return 0;
if(root->left == nullptr && root->right == nullptr) return 0;
int leftNum = sumOfLeftLeaves(root->left);
if(root->left != nullptr && root->left->left == nullptr && root->left->right == nullptr) { //这里非常精妙,如果代码能够进入,说明当前root的左孩子是一个叶子节点,那么上面的int leftNum = sumOfLeftLeaves(root->left);返回值一定是0,可以直接更新,同时这段代码也只能放在
leftNum = root->left->val; //int leftNum = sumOfLeftLeaves(root->left);之后,从而修正leftNum。是后序遍历的逻辑。
}
int rightNum = sumOfLeftLeaves(root->right);
return leftNum + rightNum;
}
};
迭代法:
迭代法需要使用栈来模拟递归过程,通过同样的判断条件找出叶子节点,然后累加val即可。
222 完全二叉树的节点个数
递归法非常简单,常规方法遍历一遍节点,只要不是空节点就将返回值加一。
此题强调了完全二叉树,利用完全二叉树中有许多子二叉树是满二叉树的特点提高时间效率。
递归法:
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == nullptr) return 0;
int leftNum = countNodes(root->left);
int rightNum = countNodes(root->right);
return 1 + leftNum + rightNum;
}
};
迭代法:
迭代法需要使用栈来模拟递归过程,通过同样的判断条件找出叶子节点,然后累加val即可。
递归法2:
每次先判断当前树是不是满二叉树,是则可以利用满二叉树节点数为 2^树深度-1 的公式直接计算,避免了因遍历所有节点而浪费时间。
class Solution {
public:
int countNodes(TreeNode* root) {
if(root == nullptr) return 0;
TreeNode* leftNode = root->left;
TreeNode* rightNode = root->right;
int leftNum = 0;
int rightNum = 0;
while(leftNode) {
leftNode = leftNode->left;
leftNum++;
}
while(rightNode) {
rightNode = rightNode->right;
rightNum++;
}
if(leftNum == rightNum) {
return (2 << leftNum) - 1;
}
return countNodes(root->left) + countNodes(root->right) + 1;
}
};

浙公网安备 33010602011771号