Given the root of a binary tree, each node in the tree has a distinct value.
After deleting all nodes with a value in to_delete, we are left with a forest (a disjoint union of trees).
Return the roots of the trees in the remaining forest. You may return the result in any order.
Example 1:

Input: root = [1,2,3,4,5,6,7], to_delete = [3,5] Output: [[1,2,null,4],[6],[7]]
Example 2:
Input: root = [1,2,4,null,3], to_delete = [3] Output: [[1,2,4]]
Constraints:
- The number of nodes in the given tree is at most
1000. - Each node has a distinct value between
1and1000. to_delete.length <= 1000to_deletecontains distinct values between1and1000.
Approach01:
-
C++
-
Python
#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
vector<TreeNode*> delNodes(TreeNode* root, vector<int>& to_delete) {
vector<TreeNode*> remainingTrees;
// Create a set from to_delete vector for quick lookup
unordered_set<int> toDeleteSet(to_delete.begin(), to_delete.end());
// Start the DFS removal process
removeNodes(root, toDeleteSet, true, remainingTrees);
return remainingTrees;
}
private:
// Helper function to perform DFS and remove nodes
TreeNode* removeNodes(TreeNode*& node, const unordered_set<int>& toDeleteSet, bool isRoot, vector<TreeNode*>& remainingTrees) {
if (node == nullptr){
return nullptr;
}
// Check if the current node needs to be deleted
const bool isDeleted = toDeleteSet.count(node->val) > 0;
// If the current node is a root and not deleted, add it to the result
if (isRoot && !isDeleted){
remainingTrees.push_back(node);
}
// Recursively process the left and right subtrees
node->left = removeNodes(node->left, toDeleteSet, isDeleted, remainingTrees);
node->right = removeNodes(node->right, toDeleteSet, isDeleted, remainingTrees);
// Return nullptr if the node is deleted, otherwise return the node
// itself
return isDeleted ? nullptr : node;
}
};
from typing import List, Optional, Set
from collections import deque
class Solution:
def delNodes(self, root: Optional[TreeNode], to_delete: List[int]) -> List[TreeNode]:
remaining_trees = []
to_delete_set = set(to_delete) # Create a set from to_delete list for quick lookup
self.removeNodes(root, to_delete_set, True, remaining_trees)
return remaining_trees
def removeNodes(self, node: Optional[TreeNode], to_delete_set: Set[int], is_root: bool, remaining_trees: List[TreeNode]) -> Optional[TreeNode]:
if node is None:
return None
# Check if the current node needs to be deleted
is_deleted = node.val in to_delete_set
# If the current node is a root and not deleted, add it to the result
if is_root and not is_deleted:
remaining_trees.append(node)
# Recursively process the left and right subtrees
node.left = self.removeNodes(node.left, to_delete_set, is_deleted, remaining_trees)
node.right = self.removeNodes(node.right, to_delete_set, is_deleted, remaining_trees)
# Return None if the node is deleted, otherwise return the node itself
return None if is_deleted else node
Time Complexity
- Creating the
toDeleteSet:Creating the
toDeleteSetfrom theto_deletevector takes \( O(m) \) time, where \( m \) is the number of elements in theto_deletevector. - DFS Traversal:
The DFS traversal of the tree visits each node exactly once, resulting in \( O(n) \) time, where \( n \) is the number of nodes in the tree.
- Overall Time Complexity:
The overall time complexity is \( O(m) + O(n) = O(n + m) \).
Space Complexity
- Auxiliary Space for
toDeleteSet:The
toDeleteSetstores up to \( m \) elements, resulting in \( O(m) \) space usage. - Recursive Call Stack:
The depth of the recursive call stack is proportional to the height of the tree, which in the worst case is \( O(n) \) for a skewed tree. For a balanced tree, it would be \( O(\log n) \).
- Overall Space Complexity:
The overall space complexity is \( O(m + n) \) in the worst case.