Skip to content

Commit

Permalink
Feat: Add word removal to the Go/struct/trie (TheAlgorithms#380)
Browse files Browse the repository at this point in the history
Co-authored-by: xavier <xavier@Fedora33OnDellXPS>
  • Loading branch information
xavier268 and xavier authored Oct 20, 2021
1 parent cfd0999 commit 9e47ef4
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 45 deletions.
24 changes: 24 additions & 0 deletions structure/set/setexample_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// package set implements a Set using a golang map.
// This implies that only the types that are accepted as valid map keys can be used as set elements.
// For instance, do not try to Add a slice, or the program will panic.
//
package set

import (
"fmt"
)

func ExampleSet() {

set := New(1, 2, 3)
fmt.Println(set.Len()) // 3
set.Add(3)
fmt.Println(set.Len()) // 3
set.Add(4)
fmt.Println(set.Len()) // 4

// output:
// 3
// 3
// 4
}
75 changes: 68 additions & 7 deletions structure/trie/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ func NewNode() *Node {
return n
}

// Insert inserts words at a Trie node.
func (n *Node) Insert(s string) {
// insert a single word at a Trie node.
func (n *Node) insert(s string) {
curr := n
for _, c := range s {
next, ok := curr.children[c]
Expand All @@ -32,15 +32,76 @@ func (n *Node) Insert(s string) {
curr.isLeaf = true
}

// Find finds words at a Trie node.
// Insert zero, one or more words at a Trie node.
func (n *Node) Insert(s ...string) {
for _, ss := range s {
n.insert(ss)
}
}

// Find words at a Trie node.
func (n *Node) Find(s string) bool {
curr := n
next, ok := n, false
for _, c := range s {
next, ok := curr.children[c]
next, ok = next.children[c]
if !ok {
return false
}
curr = next
}
return true
return next.isLeaf
}

// Capacity returns the number of nodes in the Trie
func (n *Node) Capacity() int {
r := 0
for _, c := range n.children {
r += c.Capacity()
}
return 1 + r
}

// Size returns the number of words in the Trie
func (n *Node) Size() int {
r := 0
for _, c := range n.children {
r += c.Size()
}
if n.isLeaf {
r++
}
return r
}

// remove lazily a word from the Trie node, no node is actually removed.
func (n *Node) remove(s string) {
if len(s) == 0 {
return
}
next, ok := n, false
for _, c := range s {
next, ok = next.children[c]
if !ok {
// word cannot be found - we're done !
return
}
}
next.isLeaf = false
}

// Remove zero, one or more words lazily from the Trie, no node is actually removed.
func (n *Node) Remove(s ...string) {
for _, ss := range s {
n.remove(ss)
}
}

// Compact will remove unecessay nodes, reducing the capacity, returning true if node n itself should be removed.
func (n *Node) Compact() (remove bool) {

for r, c := range n.children {
if c.Compact() {
delete(n.children, r)
}
}
return !n.isLeaf && len(n.children) == 0
}
116 changes: 116 additions & 0 deletions structure/trie/trie_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package trie

import (
"fmt"
"math/rand"
"testing"
)

// CAUTION : make sure to limit the benchamrks to 3000 iterations,
// or removal will mostly process an empty Trie, giving absurd results.

// go test -v -bench=. -benchmem -benchtime=3000x

// === RUN TestTrieInsert
// --- PASS: TestTrieInsert (0.00s)
// === RUN TestTrieRemove
// --- PASS: TestTrieRemove (0.00s)
// === RUN ExampleNode
// --- PASS: ExampleNode (0.00s)
// goos: linux
// goarch: amd64
// pkg: github.com/TheAlgorithms/Go/structure/trie
// BenchmarkTrie_Insert
// BenchmarkTrie_Insert-8 3000 1559881 ns/op 1370334 B/op 25794 allocs/op
// BenchmarkTrie_Find_non_existant
// BenchmarkTrie_Find_non_existant-8 3000 59.1 ns/op 0 B/op 0 allocs/op
// BenchmarkTrie_Find_existant
// BenchmarkTrie_Find_existant-8 3000 238 ns/op 0 B/op 0 allocs/op
// BenchmarkTrie_Remove_lazy
// BenchmarkTrie_Remove_lazy-8 3000 126 ns/op 0 B/op 0 allocs/op
// BenchmarkTrie_Remove_and_Compact
// BenchmarkTrie_Remove_and_Compact-8 3000 213945 ns/op 0 B/op 0 allocs/op
// PASS
// ok github.com/TheAlgorithms/Go/structure/trie 5.355s

func BenchmarkTrie_Insert(b *testing.B) {

// prepare random words for insertion
insert := make([]string, 3000)
for i := 0; i < len(insert); i++ {
insert[i] = fmt.Sprintf("%f", rand.Float64())
}

// Reset timer and run benchmark for insertion
b.ResetTimer()
for i := 0; i < b.N; i++ {
n := NewNode()
n.Insert(insert...)
}
}

func BenchmarkTrie_Find_non_existant(b *testing.B) {

// prepare random words for insertion
insert := make([]string, 3000)
for i := 0; i < len(insert); i++ {
insert[i] = fmt.Sprintf("%f", rand.Float64())
}
n := NewNode()
n.Insert(insert...)

// Reset timer and run benchmark for finding non existing
b.ResetTimer()
for i := 0; i < b.N; i++ {
n.Find("0.3213213244346546546546565465465") // does not exists
}
}

func BenchmarkTrie_Find_existant(b *testing.B) {
// prepare and insert random words
insert := make([]string, 3000)
for i := 0; i < len(insert); i++ {
insert[i] = fmt.Sprintf("%f", rand.Float64())
}
n := NewNode()
n.Insert(insert...)

// Reset timer and run benchmark for finding existing words
b.ResetTimer()
for i := 0; i < b.N; i++ {
n.Find(insert[i%3000]) // always exists !
}
}

func BenchmarkTrie_Remove_lazy(b *testing.B) {
// prepare and insert random words
insert := make([]string, 3000)
for i := 0; i < len(insert); i++ {
insert[i] = fmt.Sprintf("%f", rand.Float64())
}
n := NewNode()
n.Insert(insert...)

// Reset timer and run benchmark for lazily removing words
b.ResetTimer()
for i := 0; i < b.N; i++ {
n.Remove(insert[i%3000]) // exists, at least until removed ...
}
}

func BenchmarkTrie_Remove_and_Compact(b *testing.B) {
// prepare and insert random words
insert := make([]string, 3000)
for i := 0; i < len(insert); i++ {
insert[i] = fmt.Sprintf("%f", rand.Float64())
}
n := NewNode()
n.Insert(insert...)

// Reset timer and run benchmark for removing words and immediately compacting
b.ResetTimer()
for i := 0; i < b.N; i++ {
n.Remove(insert[i%3000])
n.Compact()
}
}
Loading

0 comments on commit 9e47ef4

Please sign in to comment.