二叉树剪枝

标签: 深度优先搜索 二叉树

难度: Medium

给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1

返回移除了所有不包含 1 的子树的原二叉树。

节点 node 的子树为 node 本身加上所有 node 的后代。

示例 1:

输入:root = [1,null,0,0,1]
输出:[1,null,0,null,1]
解释:
只有红色节点满足条件“所有不包含 1 的子树”。 右图为返回的答案。

示例 2:

输入:root = [1,0,1,0,0,0,1]
输出:[1,null,1,null,1]

示例 3:

输入:root = [1,1,0,1,1,0,1,0]
输出:[1,1,0,1,1,null,1]

提示:

  • 树中节点的数目在范围 [1, 200]
  • Node.val01

Submission

运行时间: 24 ms

内存: 0.0 MB

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pruneTree(self, root: TreeNode) -> TreeNode:
        def subTreeFix(node:TreeNode)->bool:
            if node:
                l=subTreeFix(node.left)
                r=subTreeFix(node.right)
                if not l:
                    node.left=None
                if not r:
                    node.right=None
                if node.val==1 or l or r:
                    return True
            return False
        
        ans=subTreeFix(root)
        if ans:
            return root
        return None

Explain

这个解法使用了后序遍历的方式来剪枝二叉树。从叶子节点开始递归,如果一个节点的左右子树都被剪枝了,并且当前节点的值为0,那么当前节点也需要被剪枝。通过后序遍历,可以保证每个节点的子树都已经被正确剪枝后,再来判断当前节点是否需要剪枝。最后,如果整棵树都被剪枝了,就返回None,否则返回根节点。

时间复杂度: O(n)

空间复杂度: O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pruneTree(self, root: TreeNode) -> TreeNode:
        def subTreeFix(node:TreeNode)->bool:
            if node:
                # 递归剪枝左子树
                l=subTreeFix(node.left) 
                # 递归剪枝右子树
                r=subTreeFix(node.right)
                # 如果左子树被剪枝,将当前节点的左指针置为None
                if not l:
                    node.left=None
                # 如果右子树被剪枝,将当前节点的右指针置为None
                if not r:
                    node.right=None
                # 如果当前节点的值为1,或者左右子树没有被完全剪枝,返回True,表示以当前节点为根的子树不需要被剪枝
                if node.val==1 or l or r:
                    return True
            # 如果当前节点为None,或者当前节点需要被剪枝,返回False
            return False
        
        # 从根节点开始剪枝
        ans=subTreeFix(root)
        # 如果整棵树都被剪枝,返回None;否则返回根节点
        if ans:
            return root
        return None

Explore

后序遍历是剪枝二叉树的合适选择,因为它允许我们先处理节点的子节点,再处理节点本身。这种方式确保在我们决定是否剪除当前节点之前,其子树已经被适当地处理和剪枝。这样,我们可以根据子节点的剪枝结果来决定当前节点是否也需要被剪枝。如果从上到下进行剪枝,我们可能会遇到需要重新考虑先前决策的情况,因为父节点的剪枝状态可能依赖于其子节点的状态。

在决定是否剪枝当前节点时,如果节点的值为1,则该节点应保留,因为它对应的是有效或重要的数据(例如在某些问题中,1可能代表特定的有效状态或特征)。即使其左右子树都被剪枝(即子节点都不重要或都是0),只要当前节点的值为1,它本身就足以保留。剪枝的目的是去除那些不影响最终结果的部分(例如值为0的无效部分),而不是去除所有非叶子节点。

是的,如果根节点的值为0且其左右子树也都被剪枝,根据提供的函数逻辑,这个根节点也会被剪除。函数中有一个检查过程,如果`subTreeFix`返回False,表示整棵树都可以被剪枝。在这种情况下,根节点也不会被保留,函数将返回None。这表明函数正确处理了这种情况,符合剪枝的预期目的。

在递归函数`subTreeFix`中,当遇到一个None的节点时返回False,这表示该节点(或其位置)不包含任何需要保留的值或结构,即它为空或者无效。返回False在逻辑上代表此位置的子树不包含任何有效的或需要保留的数据,因此不会对父节点的剪枝决策产生影响(即父节点的剪枝决策不会因为一个不存在的子节点而改变)。这有助于简化剪枝的决策过程,只关注存在且可能包含有效数据的节点。