Skip to content

Commit 9942848

Browse files
committed
Delete leaves with a given value
1 parent 3942f6e commit 9942848

File tree

2 files changed

+380
-0
lines changed

2 files changed

+380
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// 1325. Delete Leaves With a Given Value
2+
// Topics: 'Depth-First Search', 'Tree', 'Binary Search Tree', 'Binary Tree'
3+
// Level: 'Medium'
4+
5+
// Given a binary tree root and an integer target, delete all the leaf nodes with value target.
6+
7+
// Note that once you delete a leaf node with value target, if its parent node becomes a leaf node and has the value target, it should also be deleted (you need to continue doing that until you cannot).
8+
9+
// Example 1:
10+
11+
// Input: root = [1,2,3,2,null,2,4], target = 2
12+
// Output: [1,null,3,null,4]
13+
// Explanation: Leaf nodes in green with value (target = 2) are removed (Picture in left).
14+
// After removing, new nodes become leaf nodes with value (target = 2) (Picture in center).
15+
16+
// Example 2:
17+
18+
// Input: root = [1,3,3,3,2], target = 3
19+
// Output: [1,3,null,null,2]
20+
21+
// Example 3:
22+
23+
// Input: root = [1,2,null,2,null,2], target = 2
24+
// Output: [1]
25+
// Explanation: Leaf nodes in green with value (target = 2) are removed at each step.
26+
27+
// Constraints:
28+
29+
// The number of nodes in the tree is in the range [1, 3000].
30+
// 1 <= Node.val, target <= 1000
31+
32+
package deleteleaveswithagivenvalue
33+
34+
func removeLeafNodes(root *TreeNode, target int) *TreeNode {
35+
if root == nil {
36+
return nil
37+
}
38+
root.Left = removeLeafNodes(root.Left, target)
39+
root.Right = removeLeafNodes(root.Right, target)
40+
if root.Left == nil && root.Right == nil && root.Val == target {
41+
return nil
42+
}
43+
return root
44+
}
45+
46+
type TreeNode struct {
47+
Right *TreeNode
48+
Left *TreeNode
49+
Val int
50+
}
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
package deleteleaveswithagivenvalue
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestRemoveLeafNodes(t *testing.T) {
9+
tests := []struct {
10+
name string
11+
root *TreeNode
12+
target int
13+
want *TreeNode
14+
}{
15+
{
16+
name: "example 1",
17+
// 1 1
18+
// / \ \
19+
// 2 3 => 3
20+
// / / \ \
21+
// 2 2 4 4
22+
root: &TreeNode{
23+
Val: 1,
24+
Left: &TreeNode{
25+
Val: 2,
26+
Left: &TreeNode{Val: 2},
27+
},
28+
Right: &TreeNode{
29+
Val: 3,
30+
Left: &TreeNode{Val: 2},
31+
Right: &TreeNode{Val: 4},
32+
},
33+
},
34+
target: 2,
35+
want: &TreeNode{
36+
Val: 1,
37+
Left: nil,
38+
Right: &TreeNode{
39+
Val: 3,
40+
Left: nil,
41+
Right: &TreeNode{Val: 4},
42+
},
43+
},
44+
},
45+
{
46+
name: "example 2",
47+
// 1 1
48+
// / \ /
49+
// 3 3 => 3
50+
// / \ \
51+
// 3 2 2
52+
root: &TreeNode{
53+
Val: 1,
54+
Left: &TreeNode{
55+
Val: 3,
56+
Left: &TreeNode{Val: 3},
57+
Right: &TreeNode{Val: 2},
58+
},
59+
Right: &TreeNode{Val: 3},
60+
},
61+
target: 3,
62+
want: &TreeNode{
63+
Val: 1,
64+
Left: &TreeNode{
65+
Val: 3,
66+
Left: nil,
67+
Right: &TreeNode{Val: 2},
68+
},
69+
Right: nil,
70+
},
71+
},
72+
{
73+
name: "example 3",
74+
// 1 1
75+
// /
76+
// 2 =>
77+
// |
78+
// 2
79+
// |
80+
// 2
81+
root: &TreeNode{
82+
Val: 1,
83+
Left: &TreeNode{
84+
Val: 2,
85+
Left: &TreeNode{
86+
Val: 2,
87+
Left: &TreeNode{Val: 2},
88+
},
89+
},
90+
},
91+
target: 2,
92+
want: &TreeNode{Val: 1},
93+
},
94+
{
95+
name: "single node matching target",
96+
// 5 => (null)
97+
root: &TreeNode{Val: 5},
98+
target: 5,
99+
want: nil,
100+
},
101+
{
102+
name: "single node not matching target",
103+
// 5 => 5
104+
root: &TreeNode{Val: 5},
105+
target: 3,
106+
want: &TreeNode{Val: 5},
107+
},
108+
{
109+
name: "no nodes match target",
110+
// 1 1
111+
// / \ => / \
112+
// 2 3 2 3
113+
root: &TreeNode{
114+
Val: 1,
115+
Left: &TreeNode{Val: 2},
116+
Right: &TreeNode{Val: 3},
117+
},
118+
target: 5,
119+
want: &TreeNode{
120+
Val: 1,
121+
Left: &TreeNode{Val: 2},
122+
Right: &TreeNode{Val: 3},
123+
},
124+
},
125+
{
126+
name: "all leaf nodes match target",
127+
// 1 1
128+
// / \ / \
129+
// 2 4 => 2 4
130+
// / \ / \
131+
// 3 3 3 3
132+
root: &TreeNode{
133+
Val: 1,
134+
Left: &TreeNode{
135+
Val: 2,
136+
Left: &TreeNode{Val: 3},
137+
Right: &TreeNode{Val: 3},
138+
},
139+
Right: &TreeNode{
140+
Val: 4,
141+
Left: &TreeNode{Val: 3},
142+
Right: &TreeNode{Val: 3},
143+
},
144+
},
145+
target: 3,
146+
want: &TreeNode{
147+
Val: 1,
148+
Left: &TreeNode{Val: 2},
149+
Right: &TreeNode{Val: 4},
150+
},
151+
},
152+
{
153+
name: "cascading deletion left side",
154+
// 10 10
155+
// / \ \
156+
// 5 20 => 20
157+
// |
158+
// 5
159+
// |
160+
// 5
161+
root: &TreeNode{
162+
Val: 10,
163+
Left: &TreeNode{
164+
Val: 5,
165+
Left: &TreeNode{
166+
Val: 5,
167+
Left: &TreeNode{Val: 5},
168+
},
169+
},
170+
Right: &TreeNode{Val: 20},
171+
},
172+
target: 5,
173+
want: &TreeNode{
174+
Val: 10,
175+
Left: nil,
176+
Right: &TreeNode{Val: 20},
177+
},
178+
},
179+
{
180+
name: "cascading deletion right side",
181+
// 10 10
182+
// / \ /
183+
// 20 5 => 20
184+
// |
185+
// 5
186+
// |
187+
// 5
188+
root: &TreeNode{
189+
Val: 10,
190+
Left: &TreeNode{Val: 20},
191+
Right: &TreeNode{
192+
Val: 5,
193+
Right: &TreeNode{
194+
Val: 5,
195+
Right: &TreeNode{Val: 5},
196+
},
197+
},
198+
},
199+
target: 5,
200+
want: &TreeNode{
201+
Val: 10,
202+
Left: &TreeNode{Val: 20},
203+
Right: nil,
204+
},
205+
},
206+
{
207+
name: "target in middle nodes only",
208+
// 1 1
209+
// / \ / \
210+
// 2 5 => 2 5
211+
/// \ / \
212+
//3 4 3 4
213+
root: &TreeNode{
214+
Val: 1,
215+
Left: &TreeNode{
216+
Val: 2,
217+
Left: &TreeNode{Val: 3},
218+
Right: &TreeNode{Val: 4},
219+
},
220+
Right: &TreeNode{Val: 5},
221+
},
222+
target: 2,
223+
want: &TreeNode{
224+
Val: 1,
225+
Left: &TreeNode{
226+
Val: 2,
227+
Left: &TreeNode{Val: 3},
228+
Right: &TreeNode{Val: 4},
229+
},
230+
Right: &TreeNode{Val: 5},
231+
},
232+
},
233+
}
234+
235+
for _, tt := range tests {
236+
t.Run(tt.name, func(t *testing.T) {
237+
got := removeLeafNodes(tt.root, tt.target)
238+
if !treesEqual(got, tt.want) {
239+
t.Errorf("removeLeafNodes() = %v, want %v", treeToSlice(got), treeToSlice(tt.want))
240+
}
241+
})
242+
}
243+
}
244+
245+
func treesEqual(t1, t2 *TreeNode) bool {
246+
if t1 == nil && t2 == nil {
247+
return true
248+
}
249+
if t1 == nil || t2 == nil {
250+
return false
251+
}
252+
if t1.Val != t2.Val {
253+
return false
254+
}
255+
return treesEqual(t1.Left, t2.Left) && treesEqual(t1.Right, t2.Right)
256+
}
257+
258+
func treeToSlice(root *TreeNode) []interface{} {
259+
if root == nil {
260+
return []interface{}{}
261+
}
262+
result := []interface{}{}
263+
queue := []*TreeNode{root}
264+
for len(queue) > 0 {
265+
node := queue[0]
266+
queue = queue[1:]
267+
if node == nil {
268+
result = append(result, nil)
269+
} else {
270+
result = append(result, node.Val)
271+
queue = append(queue, node.Left)
272+
queue = append(queue, node.Right)
273+
}
274+
}
275+
for len(result) > 0 && result[len(result)-1] == nil {
276+
result = result[:len(result)-1]
277+
}
278+
return result
279+
}
280+
281+
func TestTraversal(t *testing.T) {
282+
tests := []struct {
283+
name string
284+
root *TreeNode
285+
target int
286+
want *TreeNode
287+
}{
288+
{
289+
name: "nil root",
290+
root: nil,
291+
target: 1,
292+
want: nil,
293+
},
294+
{
295+
name: "leaf node matches target",
296+
// 5 => (null)
297+
root: &TreeNode{Val: 5},
298+
target: 5,
299+
want: nil,
300+
},
301+
{
302+
name: "leaf node does not match target",
303+
// 5 => 5
304+
root: &TreeNode{Val: 5},
305+
target: 3,
306+
want: &TreeNode{Val: 5},
307+
},
308+
{
309+
name: "parent becomes leaf after child deletion",
310+
// 2 (null)
311+
// | =>
312+
// 2
313+
root: &TreeNode{
314+
Val: 2,
315+
Left: &TreeNode{Val: 2},
316+
},
317+
target: 2,
318+
want: nil,
319+
},
320+
}
321+
322+
for _, tt := range tests {
323+
t.Run(tt.name, func(t *testing.T) {
324+
got := removeLeafNodes(tt.root, tt.target)
325+
if !reflect.DeepEqual(got, tt.want) {
326+
t.Errorf("traversal() = %v, want %v", got, tt.want)
327+
}
328+
})
329+
}
330+
}

0 commit comments

Comments
 (0)