Skip to content

Commit 3aeef9d

Browse files
committed
Delete nodes and return forest
1 parent ba6e473 commit 3aeef9d

File tree

2 files changed

+232
-0
lines changed

2 files changed

+232
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# 1110. Delete nodes and return forest
2+
# Topics: 'Array', 'Hash Table', 'Tree', 'Depth-First Search', 'Binary Tree'
3+
# Level: 'Medium'
4+
5+
# Given the root of a binary tree, each node in the tree has a distinct value.
6+
7+
# After deleting all nodes with a value in to_delete, we are left with a forest (a disjoint union of trees).
8+
9+
# Return the roots of the trees in the remaining forest. You may return the result in any order.
10+
11+
12+
13+
# Example 1:
14+
15+
# Input: root = [1,2,3,4,5,6,7], to_delete = [3,5]
16+
# Output: [[1,2,null,4],[6],[7]]
17+
18+
# Example 2:
19+
20+
# Input: root = [1,2,4,null,3], to_delete = [3]
21+
# Output: [[1,2,4]]
22+
23+
24+
25+
# Constraints:
26+
27+
# The number of nodes in the given tree is at most 1000.
28+
# Each node has a distinct value between 1 and 1000.
29+
# to_delete.length <= 1000
30+
# to_delete contains distinct values between 1 and 1000.
31+
32+
# Definition for a binary tree node.
33+
from typing import List, Optional
34+
35+
36+
class TreeNode:
37+
def __init__(self, val=0, left=None, right=None):
38+
self.val = val
39+
self.left = left
40+
self.right = right
41+
42+
def postorder(self, root: Optional[TreeNode]):
43+
if not root:
44+
return
45+
self.postorder(root.left)
46+
self.postorder(root.right)
47+
print(root.val, end=" ")
48+
49+
class Solution:
50+
def delNodes(self, root: Optional[TreeNode], to_delete: List[int]) -> List[TreeNode]:
51+
dl = set(to_delete)
52+
res = []
53+
def dfs(root: Optional[TreeNode]) -> Optional[TreeNode]:
54+
if not root:
55+
return root
56+
root.left = dfs(root.left)
57+
root.right = dfs(root.right)
58+
if root.val in dl:
59+
if root.left:
60+
res.append(root.left)
61+
if root.right:
62+
res.append(root.right)
63+
return None
64+
return root
65+
66+
dfs(root)
67+
if root.val not in dl:
68+
res.append(root)
69+
return res
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import unittest
2+
from typing import Optional, List
3+
from delete import TreeNode, Solution
4+
5+
class TestDeleteNodes(unittest.TestCase):
6+
7+
def tree_to_list(self, root: Optional[TreeNode]) -> List:
8+
"""Helper: Convert tree to level-order list for comparison"""
9+
if not root:
10+
return []
11+
result = []
12+
queue = [root]
13+
while queue:
14+
node = queue.pop(0)
15+
if node:
16+
result.append(node.val)
17+
queue.append(node.left)
18+
queue.append(node.right)
19+
else:
20+
result.append(None)
21+
# Remove trailing Nones
22+
while result and result[-1] is None:
23+
result.pop()
24+
return result
25+
26+
def test_example_1(self):
27+
"""Test case from example 1: delete nodes 3 and 5"""
28+
# Tree: [1,2,3,4,5,6,7]
29+
root = TreeNode(1,
30+
TreeNode(2, TreeNode(4), TreeNode(5)),
31+
TreeNode(3, TreeNode(6), TreeNode(7)))
32+
33+
solution = Solution()
34+
result = solution.delNodes(root, [3, 5])
35+
36+
# Convert results to lists for easier comparison
37+
result_lists = sorted([self.tree_to_list(tree) for tree in result])
38+
39+
# Expected: [[1,2,null,4], [6], [7]]
40+
expected = sorted([[1, 2, None, 4], [6], [7]])
41+
42+
self.assertEqual(result_lists, expected)
43+
44+
def test_example_2(self):
45+
"""Test case from example 2: delete node 3"""
46+
# Tree: [1,2,4,null,3]
47+
root = TreeNode(1,
48+
TreeNode(2, None, TreeNode(3)),
49+
TreeNode(4))
50+
51+
solution = Solution()
52+
result = solution.delNodes(root, [3])
53+
54+
result_lists = [self.tree_to_list(tree) for tree in result]
55+
expected = [[1, 2, 4]]
56+
57+
self.assertEqual(result_lists, expected)
58+
59+
def test_delete_root_only(self):
60+
"""Delete only the root node"""
61+
root = TreeNode(1, TreeNode(2), TreeNode(3))
62+
63+
solution = Solution()
64+
result = solution.delNodes(root, [1])
65+
66+
result_lists = sorted([self.tree_to_list(tree) for tree in result])
67+
expected = sorted([[2], [3]])
68+
69+
self.assertEqual(result_lists, expected)
70+
71+
def test_delete_nothing(self):
72+
"""Delete no nodes - return original tree"""
73+
root = TreeNode(1, TreeNode(2), TreeNode(3))
74+
75+
solution = Solution()
76+
result = solution.delNodes(root, [])
77+
78+
self.assertEqual(len(result), 1)
79+
self.assertEqual(self.tree_to_list(result[0]), [1, 2, 3])
80+
81+
def test_delete_all_nodes(self):
82+
"""Delete all nodes in tree"""
83+
root = TreeNode(1, TreeNode(2), TreeNode(3))
84+
85+
solution = Solution()
86+
result = solution.delNodes(root, [1, 2, 3])
87+
88+
self.assertEqual(result, [])
89+
90+
def test_single_node_delete(self):
91+
"""Single node tree, delete it"""
92+
root = TreeNode(1)
93+
94+
solution = Solution()
95+
result = solution.delNodes(root, [1])
96+
97+
self.assertEqual(result, [])
98+
99+
def test_single_node_keep(self):
100+
"""Single node tree, keep it"""
101+
root = TreeNode(1)
102+
103+
solution = Solution()
104+
result = solution.delNodes(root, [])
105+
106+
self.assertEqual(len(result), 1)
107+
self.assertEqual(result[0].val, 1)
108+
109+
def test_delete_leaf_nodes(self):
110+
"""Delete only leaf nodes"""
111+
root = TreeNode(1,
112+
TreeNode(2, TreeNode(4), TreeNode(5)),
113+
TreeNode(3))
114+
115+
solution = Solution()
116+
result = solution.delNodes(root, [4, 5, 3])
117+
118+
self.assertEqual(len(result), 1)
119+
self.assertEqual(self.tree_to_list(result[0]), [1, 2])
120+
121+
def test_delete_internal_nodes(self):
122+
"""Delete internal nodes creating multiple forests"""
123+
root = TreeNode(1,
124+
TreeNode(2, TreeNode(4), TreeNode(5)),
125+
TreeNode(3, TreeNode(6), TreeNode(7)))
126+
127+
solution = Solution()
128+
result = solution.delNodes(root, [2, 3])
129+
130+
result_lists = sorted([self.tree_to_list(tree) for tree in result])
131+
expected = sorted([[1], [4], [5], [6], [7]])
132+
133+
self.assertEqual(result_lists, expected)
134+
135+
def test_linear_tree_delete_middle(self):
136+
"""Linear tree (like linked list), delete middle node"""
137+
root = TreeNode(1, TreeNode(2, TreeNode(3, TreeNode(4))))
138+
139+
solution = Solution()
140+
result = solution.delNodes(root, [2])
141+
142+
result_lists = sorted([self.tree_to_list(tree) for tree in result])
143+
expected = sorted([[1], [3, 4]])
144+
145+
self.assertEqual(result_lists, expected)
146+
147+
def test_delete_node_with_one_child(self):
148+
"""Delete node that has only one child"""
149+
root = TreeNode(1,
150+
TreeNode(2, TreeNode(4)),
151+
TreeNode(3))
152+
153+
solution = Solution()
154+
result = solution.delNodes(root, [2])
155+
156+
result_lists = sorted([self.tree_to_list(tree) for tree in result])
157+
expected = sorted([[1, None, 3], [4]])
158+
159+
self.assertEqual(result_lists, expected)
160+
161+
162+
if __name__ == '__main__':
163+
unittest.main(verbosity=2)

0 commit comments

Comments
 (0)