Delete Nodes And Return Forest
Post-order DFS — a node whose value is to delete returns null and pushes its non-null children as roots.
Problem#
Given the root of a binary tree with distinct values and a list of values to delete, return all the roots of the resulting forest.
Examples#
root = [1,2,3,4,5,6,7], to_delete = [3,5]→ roots of[1,2,null,4]and[6]and[7].root = [1,2,4,null,3], to_delete = [3]→ roots of[1,2,4].
Constraints#
- The number of nodes is in
[1, 1000].to_delete.length <= 1000.
Approach#
Post-order DFS. A helper takes a flag isRoot (true at start). If the node’s value is to delete, its non-null children become roots and we return null. Otherwise, if isRoot, add this node to the forest; recurse with isRoot corresponding to “parent was deleted”.
Solution#
class Solution {public: vector<TreeNode*> delNodes(TreeNode* root, vector<int>& to_delete) { unordered_set<int> bad(to_delete.begin(), to_delete.end()); vector<TreeNode*> forest; function<TreeNode*(TreeNode*, bool)> dfs = [&](TreeNode* u, bool isRoot) -> TreeNode* { if (!u) return nullptr; bool del = bad.count(u->val); if (isRoot && !del) forest.push_back(u); u->left = dfs(u->left, del); u->right = dfs(u->right, del); return del ? nullptr : u; }; dfs(root, true); return forest; }};func delNodes(root *TreeNode, toDelete []int) []*TreeNode { bad := make(map[int]bool, len(toDelete)) for _, v := range toDelete { bad[v] = true } forest := []*TreeNode{} var dfs func(u *TreeNode, isRoot bool) *TreeNode dfs = func(u *TreeNode, isRoot bool) *TreeNode { if u == nil { return nil } del := bad[u.Val] if isRoot && !del { forest = append(forest, u) } u.Left = dfs(u.Left, del) u.Right = dfs(u.Right, del) if del { return nil } return u } dfs(root, true) return forest}from typing import List, Optional
class Solution: def delNodes(self, root: Optional['TreeNode'], to_delete: List[int]) -> List['TreeNode']: bad = set(to_delete) forest: List['TreeNode'] = []
def dfs(u: Optional['TreeNode'], is_root: bool) -> Optional['TreeNode']: if not u: return None delete = u.val in bad if is_root and not delete: forest.append(u) u.left = dfs(u.left, delete) u.right = dfs(u.right, delete) return None if delete else u
dfs(root, True) return forestfunction delNodes(root, to_delete) { const bad = new Set(to_delete); const forest = []; const dfs = (u, isRoot) => { if (!u) return null; const del = bad.has(u.val); if (isRoot && !del) forest.push(u); u.left = dfs(u.left, del); u.right = dfs(u.right, del); return del ? null : u; }; dfs(root, true); return forest;}class Solution { private Set<Integer> bad; private List<TreeNode> forest;
public List<TreeNode> delNodes(TreeNode root, int[] to_delete) { bad = new HashSet<>(); for (int v : to_delete) bad.add(v); forest = new ArrayList<>(); dfs(root, true); return forest; }
private TreeNode dfs(TreeNode u, boolean isRoot) { if (u == null) return null; boolean del = bad.contains(u.val); if (isRoot && !del) forest.add(u); u.left = dfs(u.left, del); u.right = dfs(u.right, del); return del ? null : u; }}function delNodes(root: TreeNode | null, to_delete: number[]): Array<TreeNode | null> { const bad = new Set<number>(to_delete); const forest: Array<TreeNode | null> = []; const dfs = (u: TreeNode | null, isRoot: boolean): TreeNode | null => { if (!u) return null; const del = bad.has(u.val); if (isRoot && !del) forest.push(u); u.left = dfs(u.left, del); u.right = dfs(u.right, del); return del ? null : u; }; dfs(root, true); return forest;}Editorial#
A node is the root of a new tree iff (a) it’s not deleted and (b) its parent was either absent (real root) or deleted. The isRoot flag plus current del decision captures both states. Children process the post-order rewrite — surviving subtree roots get reattached.
Complexity#
- Time: O(n).
- Space: O(h).
Concept revision#
Related#