Implement hash set
This commit is contained in:
parent
031c9f6be8
commit
acae9a6b41
5 changed files with 234 additions and 25 deletions
92
hash_set/hash_set/hash_set.go
Normal file
92
hash_set/hash_set/hash_set.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package hash_set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyHash interface {
|
||||||
|
~string | ~int | ~uint | ~int64 | ~uint64 | ~int32 | ~uint32 | ~int16 | ~uint16 | ~int8 | ~uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hasher[H MyHash] interface {
|
||||||
|
Hash() H
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyHashSet is a hash set implementation.
|
||||||
|
type MyHashSet[T Hasher[H], H MyHash] struct {
|
||||||
|
storage map[H]T
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSet initializes a new hash set.
|
||||||
|
func NewSet[T Hasher[H], H MyHash]() MyHashSet[T, H] {
|
||||||
|
return MyHashSet[T, H]{
|
||||||
|
storage: make(map[H]T, 100),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds an element to a set.
|
||||||
|
func (s *MyHashSet[T, H]) Add(element T) {
|
||||||
|
// Hash element
|
||||||
|
hash := element.Hash()
|
||||||
|
|
||||||
|
// Save
|
||||||
|
_, ok := s.storage[hash]
|
||||||
|
if !ok {
|
||||||
|
s.storage[hash] = element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAll adds all the elements to the set.
|
||||||
|
func (s *MyHashSet[T, H]) AddAll(elements ...T) {
|
||||||
|
for _, element := range elements {
|
||||||
|
s.Add(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains checks if the hash set contains the element T.
|
||||||
|
// Returns true if the element is part of the set, false otherwise.
|
||||||
|
func (s *MyHashSet[T, H]) Contains(element T) bool {
|
||||||
|
// Hash element
|
||||||
|
hash := element.Hash()
|
||||||
|
_, ok := s.storage[hash]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes an element from the set.
|
||||||
|
// Returns true if the element was deleted, false otherwise.
|
||||||
|
func (s *MyHashSet[T, H]) Delete(element T) bool {
|
||||||
|
// Hash element
|
||||||
|
hash := element.Hash()
|
||||||
|
_, ok := s.storage[hash]
|
||||||
|
if ok {
|
||||||
|
delete(s.storage, hash)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the set.
|
||||||
|
func (s *MyHashSet[T, H]) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteString("MyHashSet{")
|
||||||
|
for _, value := range s.storage {
|
||||||
|
sb.WriteString(fmt.Sprintf("%v,", value))
|
||||||
|
}
|
||||||
|
sb.WriteString("}")
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union creates a union of two sets
|
||||||
|
func (s *MyHashSet[T, H]) Union(other *MyHashSet[T, H]) {
|
||||||
|
for _, v := range other.storage {
|
||||||
|
s.Add(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub creates a difference of two sets
|
||||||
|
func (s *MyHashSet[T, H]) Sub(other *MyHashSet[T, H]) {
|
||||||
|
for _, v := range other.storage {
|
||||||
|
s.Delete(v)
|
||||||
|
}
|
||||||
|
}
|
85
hash_set/hash_set/hash_set_test.go
Normal file
85
hash_set/hash_set/hash_set_test.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package hash_set
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyString struct {
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MyString) Hash() string {
|
||||||
|
return m.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSet(t *testing.T) {
|
||||||
|
newSet := NewSet[MyString, string]()
|
||||||
|
assert.NotNil(t, newSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMyHashSet_Add(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
newSet := NewSet[MyString, string]()
|
||||||
|
newSet.Add(MyString{Value: "some"})
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "some"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMyHashSet_AddAll(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
newSet := NewSet[MyString, string]()
|
||||||
|
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"})
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "some"}))
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "another"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMyHashSet_Delete(t *testing.T) {
|
||||||
|
// Given
|
||||||
|
newSet := NewSet[MyString, string]()
|
||||||
|
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"})
|
||||||
|
newSet.Delete(MyString{Value: "some"})
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assert.False(t, newSet.Contains(MyString{Value: "some"}))
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "another"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMyHashSet_Union(t *testing.T) {
|
||||||
|
// Setup
|
||||||
|
newSet := NewSet[MyString, string]()
|
||||||
|
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"})
|
||||||
|
|
||||||
|
anotherSet := NewSet[MyString, string]()
|
||||||
|
anotherSet.AddAll(MyString{Value: "Batman"}, MyString{Value: "Robin"})
|
||||||
|
|
||||||
|
// Test
|
||||||
|
newSet.Union(&anotherSet)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "some"}))
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "another"}))
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "Batman"}))
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "Robin"}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMyHashSet_Sub(t *testing.T) {
|
||||||
|
// Setup
|
||||||
|
newSet := NewSet[MyString, string]()
|
||||||
|
newSet.AddAll(MyString{Value: "some"}, MyString{Value: "another"}, MyString{Value: "Batman"})
|
||||||
|
|
||||||
|
anotherSet := NewSet[MyString, string]()
|
||||||
|
anotherSet.AddAll(MyString{Value: "Batman"}, MyString{Value: "Robin"})
|
||||||
|
|
||||||
|
// Test
|
||||||
|
newSet.Sub(&anotherSet)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "some"}))
|
||||||
|
assert.True(t, newSet.Contains(MyString{Value: "another"}))
|
||||||
|
assert.False(t, newSet.Contains(MyString{Value: "Batman"}))
|
||||||
|
assert.False(t, newSet.Contains(MyString{Value: "Robin"}))
|
||||||
|
}
|
51
hash_set/main.go
Normal file
51
hash_set/main.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go-dsa/hash_set/v2/hash_set"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Person struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the has of a person, it conforms to the hash_set.Hasher interface
|
||||||
|
func (p Person) Hash() string {
|
||||||
|
return fmt.Sprintf("%s-%d", p.Name, p.Age)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mySet := hash_set.NewSet[Person, string]()
|
||||||
|
|
||||||
|
// Add
|
||||||
|
mySet.Add(Person{
|
||||||
|
Name: "Batman",
|
||||||
|
Age: 28,
|
||||||
|
})
|
||||||
|
mySet.Add(Person{
|
||||||
|
Name: "Robin",
|
||||||
|
Age: 16,
|
||||||
|
})
|
||||||
|
mySet.Add(Person{
|
||||||
|
Name: "Batman",
|
||||||
|
Age: 28,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Print
|
||||||
|
fmt.Printf("%s\n", mySet.String())
|
||||||
|
|
||||||
|
// Contains
|
||||||
|
result := mySet.Contains(Person{
|
||||||
|
Name: "Batman",
|
||||||
|
Age: 28,
|
||||||
|
})
|
||||||
|
fmt.Printf("Set contains batman %v\n", result)
|
||||||
|
|
||||||
|
// Deletion
|
||||||
|
mySet.Delete(Person{
|
||||||
|
Name: "Batman",
|
||||||
|
Age: 28,
|
||||||
|
})
|
||||||
|
fmt.Printf("%s\n", mySet.String())
|
||||||
|
}
|
25
main.go
25
main.go
|
@ -1,25 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
//TIP To run your code, right-click the code and select <b>Run</b>. Alternatively, click
|
|
||||||
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here.
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
//TIP Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined or highlighted text
|
|
||||||
// to see how GoLand suggests fixing it.
|
|
||||||
s := "gopher"
|
|
||||||
fmt.Println("Hello and welcome, %s!", s)
|
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
|
||||||
//TIP You can try debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
|
|
||||||
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>. To start your debugging session,
|
|
||||||
// right-click your code in the editor and select the <b>Debug</b> option.
|
|
||||||
fmt.Println("i =", 100/i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TIP See GoLand help at <a href="https://www.jetbrains.com/help/go/">jetbrains.com/help/go/</a>.
|
|
||||||
// Also, you can try interactive lessons for GoLand by selecting 'Help | Learn IDE Features' from the main menu.
|
|
6
readme.md
Normal file
6
readme.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Data Structures and Algorithms
|
||||||
|
|
||||||
|
This repository is my self study guide for data structures and algorithms. I implement them from scratch in Go
|
||||||
|
and then write unit test for them.
|
||||||
|
|
||||||
|
What better way to learn a language and new concepts exists other than practicing them.
|
Loading…
Reference in a new issue