Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions data-structures/dictionary/dictionary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Package dictionary is word dictionary implementation using trie in go
package dictionary

// trieNode saves trie structure
type trieNode struct {
childMap map[rune]*trieNode
isEnd bool
}

// Dictionary struct contains data and methods
type Dictionary struct {
root *trieNode
wordCount int
}

// NewDictionary creates new instance of dictionary
func NewDictionary() *Dictionary {
return &Dictionary{
root: &trieNode{
childMap: map[rune]*trieNode{},
isEnd: false,
},
wordCount: 0,
}
}

// Size returns word count in dictionary
func (pr *Dictionary) Size() int {
return pr.wordCount
}

// Insert inserts new word in dictionary
// returns false if this word already present
func (pr *Dictionary) Insert(word string) bool {
node := pr.root
for _, ch := range word {
if newNode, ok := node.childMap[ch]; ok {
node = newNode
} else {
node.childMap[ch] = &trieNode{
childMap: map[rune]*trieNode{},
isEnd: false,
}
node = node.childMap[ch]
}
}
if node.isEnd {
return false
}
node.isEnd = true
pr.wordCount++
return true
}

// InsertAll inserts all words in dictionary
// returns number of words actually inserted
func (pr *Dictionary) InsertAll(words []string) int {
var res = 0
for _, word := range words {
if pr.Insert(word) {
res++
}
}
return res
}

// Retrieve retrieves all words in dictionary starting with prefix
func (pr *Dictionary) Retrieve(prefix string) []string {
node, depth := longestMatch(prefix, pr.root)
if depth != len(prefix) {
return []string{}
}
return allChild(prefix, node)
}

// Contains checks if given word is in dictionary
func (pr *Dictionary) Contains(word string) bool {
node, depth := longestMatch(word, pr.root)
return depth == len(word) && node.isEnd
}

// Delete deletes given word from dictionary
// returns false if word is'n in dictionary
func (pr *Dictionary) Delete(word string) bool {
node, depth := longestMatch(word, pr.root)
if depth == len(word) && node.isEnd {
node.isEnd = false
return true
}
return false
}

func longestMatch(prefix string, root *trieNode) (*trieNode, int) {
node := root
var depth = 0
for _, ch := range prefix {
if newNode, ok := node.childMap[ch]; ok {
node = newNode
depth++
} else {
return node, depth
}
}
return node, depth
}

func allChild(prefix string, node *trieNode) []string {
var res []string
if node.isEnd {
res = append(res, prefix)
}

for ch, childNode := range node.childMap {
newStr := prefix + string(ch)
res = append(res, allChild(newStr, childNode)...)
}
return res
}
142 changes: 142 additions & 0 deletions data-structures/dictionary/dictionary_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package dictionary

import (
"testing"

"github.com/RincLiu/Go-Algorithm/data-structures/dictionary"
)

func TestEmptyDictionary(t *testing.T) {
var dict = dictionary.NewDictionary()
if dict.Size() != 0 {
t.Errorf("dictionary should be empty")
}

}
func TestInsertingSame(t *testing.T) {
var dict = dictionary.NewDictionary()

if dict.Insert("abc") == false {
t.Errorf("dictionary should be empty")
}
if dict.Insert("abc") == true {
t.Errorf("abc should already be in dictionary")
}

}

func TestRetrieve(t *testing.T) {
var dict = dictionary.NewDictionary()

if dict.Insert("abc") == false {
t.Errorf("dictionary should be empty")
}
if dict.Insert("abb") == false {
t.Errorf("abb should't be in directory")
}
if dict.Insert("abcc") == false {
t.Errorf("abcc should't be in directory")
}

if len(dict.Retrieve("a")) != 3 {
t.Errorf("there sould be 3 words starting with a")
}
if len(dict.Retrieve("ab")) != 3 {
t.Errorf("there sould be 3 words starting with ab")
}
if len(dict.Retrieve("abb")) != 1 {
t.Errorf("there sould be 1 words starting with abb")
}
if len(dict.Retrieve("gg")) != 0 {
t.Errorf("there sould be 0 words starting with gg")
}

}

func TestContains(t *testing.T) {
var dict = dictionary.NewDictionary()
if dict.Insert("abc") == false {
t.Errorf("dictionary should be empty")
}
if dict.Contains("abc") == false {
t.Errorf("abc should be in dictionary")
}

var words = []string{
"aaa",
"b",
"bbc",
"ccccdddd",
"d",
"ddaaaa",
"dddddddeeeeeeaaaa",
"eabbbb",
"eaccbbbbb",
}

var containsTests = []struct {
word string // input
expected bool // expected result
}{
{"aaa", true},
{"ccccdddd", true},
{"sdfdsf", false},
{"eaccbbbba", false},
{"", false},
{"eabbbb", true},
{"bb", false},
}
dict.InsertAll(words)

for _, tt := range containsTests {
actual := dict.Contains(tt.word)
if actual != tt.expected {
t.Errorf("contains(%s): expected %t, actual %t", tt.word, tt.expected, actual)
}
}

}
func TestDelete(t *testing.T) {
var dict = dictionary.NewDictionary()
if dict.Insert("abc") == false {
t.Errorf("dictionary should be empty")
}
if dict.Delete("abc") == false {
t.Errorf("abc should be in dictionary")
}

var words = []string{
"aaa",
"b",
"bbc",
"ccccdddd",
"d",
"ddaaaa",
"dddddddeeeeeeaaaa",
"eabbbb",
"eaccbbbbb",
}

var containsTests = []struct {
word string // input
expected bool // expected result
}{
{"aaa", true},
{"aaa", false},
{"sdfdsf", false},
{"eaccbbbba", false},
{"", false},
{"eabbbb", true},
{"eabbbb", false},
{"bb", false},
}
dict.InsertAll(words)

for _, tt := range containsTests {
actual := dict.Delete(tt.word)
if actual != tt.expected {
t.Errorf("delete(%s): expected %t, actual %t", tt.word, tt.expected, actual)
}
}

}