一.题目

二.思路讲解
2.1 审题
本题要求对一棵二叉树进行剪枝 ,其中每个节点的值只能是 0 或 1 。剪枝的规则是:如果一棵子树中所有节点的值都为 0 ,那么将这棵子树整个移除。换句话说,我们需要从叶子节点向上检查,如果某个节点及其所有后代都不包含 1,则将该节点置为 nullptr,使其父节点不再指向它。
2.2 子问题解决
对于二叉树的问题,我们通常采用后序遍历的方式,因为要先知道左右子树的情况,才能决定当前节点是否需要被剪掉。具体思路如下:
-
首先,递归地处理左子树 和右子树,得到它们剪枝后的结果。这一步我们相信递归能正确完成。
-
然后,检查当前节点:如果左子树剪枝后为空 且右子树剪枝后为空 且当前节点的值本身为 0 ,说明以当前节点为根的整棵子树都不包含 1,那么应该将当前节点删除,即返回
nullptr给父节点。 -
否则,当前节点需要保留,并连接上剪枝后的左右子树,然后返回当前节点。
通过这种方式,我们自底向上地完成了剪枝,且每个节点只需要访问一次。
2.3 递归终止条件
递归的终止条件是当节点为空(nullptr)时,直接返回 nullptr。因为空节点既没有值也不属于任何子树,作为递归的基础情况非常自然。
三.代码演示
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* pruneTree(TreeNode* root)
{
//1.终止条件
if(root == nullptr)
return nullptr;
//2.子问题解决
root->left = pruneTree(root->left);
root->right = pruneTree(root->right);
if(root->left == nullptr && root->right == nullptr && root->val == 0)
{
return nullptr;
}
return root;
}
};
四.代码讲解
一、递归函数设计
我们定义递归函数 pruneTree(TreeNode* root) ,它的含义是:对以 root 为根的二叉树进行剪枝,并返回剪枝后新的根节点(可能为 nullptr)。剪枝规则是移除所有不包含 1 的子树。由于我们需要先知道左右子树的情况才能决定当前节点是否保留,因此采用后序遍历的方式。
二、递归终止条件
当当前节点为空(root == nullptr)时,直接返回 nullptr。这是递归的基础情况,因为空节点自然不需要剪枝,也没有任何值。
三、递归步骤分解
对于非空节点,按照以下步骤进行后序遍历:
-
递归处理左子树 调用
pruneTree(root->left)对左子树进行剪枝,并将结果重新赋值给root->left。这一步会移除左子树中所有全零子树,返回剪枝后的左子节点(可能为空)。我们相信递归能正确完成。 -
递归处理右子树 同理,调用
pruneTree(root->right)对右子树剪枝,结果赋给root->right。 -
判断当前节点是否需要被移除 在左右子树都处理完毕后,检查当前节点是否满足移除条件 :左子树为空、右子树为空、且当前节点的值为
0。如果三者同时成立,说明以当前节点为根的整棵子树都不包含1,因此应该将当前节点移除,返回nullptr给父节点。 -
返回保留的节点 如果不满足移除条件,则当前节点需要保留,直接返回
root。此时它的左右子树已经过剪枝,结构正确。
四、关键细节
-
后序遍历的必要性:只有先知道左右子树是否为空,才能判断当前节点是否应该被删除。这是由剪枝规则决定的。。
-
指针更新 :通过
root->left = pruneTree(root->left)的方式,将剪枝后的子树直接挂回当前节点,实现了原地修改。 -
移除条件:注意条件是左右子树都为空且当前节点值为0,这意味着整棵子树都无1。如果当前节点值为1,即使左右为空也保留;如果某子树非空,说明该子树有1,当前节点也保留。