Problem of The Day: Delete Leaves With a Given Value
Problem Statement
Intuition
To solve the problem of removing leaf nodes with a specific target value from a binary tree, we need to recursively check each node’s children. If a node becomes a leaf node and its value equals the target value, we need to remove it. This removal process should continue until no such nodes remain.
Approach
- Recursive Traversal: Traverse the tree using post-order traversal (left, right, root) because we need to handle the children before their parent. This ensures that we can safely remove leaf nodes and check if their parent becomes a leaf node after their removal.
- Remove Leaf Nodes: If a node is a leaf node and its value is equal to the target value, we can remove it by returning None from the recursive call.
- Return Updated Tree: The final tree is returned after all necessary removals.
Complexity
-
Time complexity: O(n) where n is the number of nodes in the tree
-
Space complexity: O(h) where h is the height of the tree
Code
# 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 removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]:
def helper(node):
if not node:
return
L = helper(node.left)
R = helper(node.right)
if node.val == target:
if not L and not R:
node.val = 'x'
if not L and R and R.val == 'x':
node.val = 'x'
if L and L.val == 'x' and not R:
node.val = 'x'
if L and R and L.val == 'x' and R.val == 'x':
node.val = 'x'
return node
def delete_node(node):
if not node:
return
if node.left and node.left.val == 'x':
node.left = None
if node.right and node.right.val == 'x':
node.right = None
delete_node(node.left)
delete_node(node.right)
helper(root)
delete_node(root)
return root if root.val != 'x' else None
Editorial Solution
Approach 1: Recursion (Postorder Traversal)
class Solution:
def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]:
# Base case
if root is None:
return None
# 1. Visit the left children
root.left = self.removeLeafNodes(root.left, target)
# 2. Visit the right children
root.right = self.removeLeafNodes(root.right, target)
# 3. Check if the current node is a leaf node and its value equals target
if root.left is None and root.right is None and root.val == target:
# Delete the node by returning None to the parent, effectively disconnecting it
return None
# Keep it untouched otherwise
return root
Approach 2: Iterative (PostOrder Traversal)
class Solution:
def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]:
if not root:
return None
stack = []
current_node = root
last_right_node = None
while stack or current_node:
# Push left nodes to the stack until reaching the leftmost node.
while current_node:
stack.append(current_node)
current_node = current_node.left
# Access the top node on the stack without removing it.
# This node is the current candidate for processing.
current_node = stack[-1]
# Check if the current node has an unexplored right subtree.
# If so, shift to the right subtree unless it's the subtree we just visited.
if current_node.right and current_node.right is not last_right_node:
current_node = current_node.right
continue # Continue in the while loop to push this new subtree's leftmost nodes.
# Remove the node from the stack, since we're about to process it.
stack.pop()
# If the node has no right subtree or the right subtree has already been visited,
# then check if it is a leaf node with the target value.
if not current_node.right and not current_node.left and current_node.val == target:
# If the stack is empty after popping, it means the root was a target leaf node.
if not stack:
return None # The tree becomes empty as the root itself is removed.
# Identify the parent of the current node.
parent = stack[-1] if stack else None
# Disconnect the current node from its parent.
if parent and parent.left is current_node:
parent.left = None # If current is a left child, set the left pointer to null.
elif parent and parent.right is current_node:
parent.right = None # If current is a right child, set the right pointer to null.
# Mark this node as visited by setting 'last_right_node' to 'current_node' before moving to the next iteration.
last_right_node = current_node
# Reset 'current_node' to None to ensure the next node from the stack is processed.
current_node = None
return root # Return the modified tree