python, recursive, clean, explained in details with tips, 10 lines, fast
\nclass Solution:\n    def delNodes(self, root: TreeNode, to_delete: List[int]) -> List[TreeNode]:       \n        ans,to_delete = [],set(to_delete)\n        de
```\nclass Solution:\n    def delNodes(self, root: TreeNode, to_delete: List[int]) -> List[TreeNode]:       \n        ans,to_delete = [],set(to_delete)\n        def f(r):\n            if r:\n                r.left,r.right = f(r.left), f(r.right)\n                if r.val not in to_delete:\n                    return r\n                ans.append(r.left)\n                ans.append(r.right)\n        ans.append(f(root))            \n        return([ a for a in ans if a ])\n```\n\nThe general idea:\n\nWe need to keep the tree intact to traverse all the way down first. \n\nThen on the way back up we check if the node value is in the list `to_delete`. If this is true, we add the subtrees to the results list `ans` and return `None` to the next level up. Otherwise we return the current node. And then we use the return values on the level up to either keep the tree connected or break it up.\n\nTo reduce number of checks we add all subtrees to `ans` even if they are empty and then we remove the empty element at the end before we return the result.\n\nHere is a line by line explanation:\n\nThis is our result list:\n\n```\n        ans,to_delete = [],set(to_delete)\n```\n\nBy the way, why do we convert `to_delete` into a set? Since we are going to check if a value is in that list, it will be much faster if we use a set since that lookup will be a hash based instead of a scan based.\n\nA helper function `f()`. It takes a tree root `r` and return either the same tree root back if the node value is not in `to_delete`, or `Null` if that subtree was detached:\n\n```\n        def f(r):\n```\n\nTo end recursion we check if the root is not empty:\n\n```\n            if r:\n```\n\nNow we assign the new values to r.left and r.right. Keep in mind that while we put that assignment in the very beginning of the function, the assignment will be actually completed after all recursive calls below return values, so it will be done on the way up:\n\n```\n                r.left,r.right = f(r.left), f(r.right)\n```\n\nSo at this point the subtrees below have been processed, so we need to process the current value. If that node is not supposed to be deleted, we return it as is:\n\n```\n                if r.val not in to_delete:\n                    return r\n```\n\nOtherwise we add the subtrees below into the result forest. The `r.left`, `r.right` could be `None`.\n\n```\n                ans.append(r.left)\n                ans.append(r.right)\n```\n\nHere is a trick. We grouped returns in such way that the very last line is supposed to return `None`. But that is what Python functions do by default, so we can save a line!\n\nNow we are back to the main fuction.  Here we call the helper function and add the return value to the result forest:\n\n```\n        ans.append(f(root)) \n```\n\nFinally, we remove the empty values and return:\n\n```\n        return([ a for a in ans if a ])\n```\n
[]