Skip to content

Commit 1fca145

Browse files
authored
Merge pull request #7 from preetapan/master
Implement DeletePrefix for feature parity with go-immutable-radix, pl…
2 parents 4239b77 + 1d2376b commit 1fca145

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

‎radix.go‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,53 @@ DELETE:
292292
return leaf.val, true
293293
}
294294

295+
// DeletePrefix is used to delete the subtree under a prefix
296+
// Returns how many nodes were deleted
297+
// Use this to delete large subtrees efficiently
298+
func (t *Tree) DeletePrefix(s string) int {
299+
return t.deletePrefix(nil, t.root, s)
300+
}
301+
302+
// delete does a recursive deletion
303+
func (t *Tree) deletePrefix(parent, n *node, prefix string) int {
304+
// Check for key exhaustion
305+
if len(prefix) == 0 {
306+
// Remove the leaf node
307+
subTreeSize := 0
308+
//recursively walk from all edges of the node to be deleted
309+
recursiveWalk(n, func(s string, v interface{}) bool {
310+
subTreeSize++
311+
return false
312+
})
313+
if n.isLeaf() {
314+
n.leaf = nil
315+
}
316+
n.edges = nil // deletes the entire subtree
317+
318+
// Check if we should merge the parent's other child
319+
if parent != nil && parent != t.root && len(parent.edges) == 1 && !parent.isLeaf() {
320+
parent.mergeChild()
321+
}
322+
t.size -= subTreeSize
323+
return subTreeSize
324+
}
325+
326+
// Look for an edge
327+
label := prefix[0]
328+
child := n.getEdge(label)
329+
if child == nil || (!strings.HasPrefix(child.prefix, prefix) && !strings.HasPrefix(prefix, child.prefix)) {
330+
return 0
331+
}
332+
333+
// Consume the search prefix
334+
if len(child.prefix) > len(prefix) {
335+
prefix = prefix[len(prefix):]
336+
} else {
337+
prefix = prefix[len(child.prefix):]
338+
}
339+
return t.deletePrefix(n, child, prefix)
340+
}
341+
295342
func (n *node) mergeChild() {
296343
e := n.edges[0]
297344
child := e.node

‎radix_test.go‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,46 @@ func TestDelete(t *testing.T) {
104104
}
105105
}
106106

107+
func TestDeletePrefix(t *testing.T) {
108+
type exp struct {
109+
inp[] string
110+
prefix string
111+
out[] string
112+
numDeleted int
113+
}
114+
115+
cases := []exp{
116+
{[]string{"", "A", "AB", "ABC", "R", "S"}, "A", []string{"", "R", "S"}, 3},
117+
{[]string{"", "A", "AB", "ABC", "R", "S"}, "ABC", []string{"", "A", "AB", "R", "S"}, 1},
118+
{[]string{"", "A", "AB", "ABC", "R", "S"}, "", []string{}, 6},
119+
{[]string{"", "A", "AB", "ABC", "R", "S"}, "S", []string{"", "A", "AB", "ABC", "R"}, 1},
120+
{[]string{"", "A", "AB", "ABC", "R", "S"}, "SS", []string{"", "A", "AB", "ABC", "R", "S"}, 0},
121+
}
122+
123+
for _, test := range cases {
124+
r := New()
125+
for _, ss := range test.inp {
126+
r.Insert(ss, true)
127+
}
128+
129+
deleted := r.DeletePrefix(test.prefix)
130+
if deleted != test.numDeleted {
131+
t.Fatalf("Bad delete, expected %v to be deleted but got %v", test.numDeleted, deleted)
132+
}
133+
134+
out := []string{}
135+
fn := func(s string, v interface{}) bool {
136+
out = append(out, s)
137+
return false
138+
}
139+
r.Walk(fn)
140+
141+
if !reflect.DeepEqual(out, test.out) {
142+
t.Fatalf("mis-match: %v %v", out, test.out)
143+
}
144+
}
145+
}
146+
107147
func TestLongestPrefix(t *testing.T) {
108148
r := New()
109149

0 commit comments

Comments
 (0)