Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
zyedidia committed Dec 15, 2021
0 parents commit 997beff
Show file tree
Hide file tree
Showing 12 changed files with 829 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
todo.txt
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generic Datastructures

With the release of Go 1.18, it is possible to implement generic data
structures in Go. This repository contains some data structures I have found
useful. See the individual directories for more information about each data
structure.

* `avl`: an AVL tree.
* `cache`: a wrapper around `map[K]V` that uses a maximum size and evicts
elements using LRU when full.
* `hashmap`: a hashmap with linear probing. The main feature is that
the hashmap can be efficiently copied, using copy-on-write under the hood.
* `list`: a doubly-linked list.
* `rope`: a generic rope, which is similar to an array but supports efficient
insertion and deletion from anywhere in the array. Ropes are typically used
for arrays of bytes, but this rope is generic.
* `stack`: a LIFO stack.

This project is currently in-progress.
165 changes: 165 additions & 0 deletions avl/avl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package avl

import "github.com/zyedidia/generic"

type Tree[K generic.Orderable[K], V any] struct {
root *node[K, V]
}

func (t *Tree[K, V]) Add(key K, value V) {
t.root = t.root.add(key, value)
}

func (t *Tree[K, V]) Remove(key K) {
t.root.remove(key)
}

func (t *Tree[K, V]) Search(key K) (V, bool) {
n := t.root.search(key)
if n == nil {
var v V
return v, false
}
return n.value, true
}

func (t *Tree[K, V]) Height() int {
return t.root.getHeight()
}

type node[K generic.Orderable[K], V any] struct {
key K
value V

height int
left *node[K, V]
right *node[K, V]
}

func (n *node[K, V]) add(key K, value V) *node[K, V] {
if n == nil {
return &node[K, V]{
key: key,
value: value,
height: 1,
left: nil,
right: nil,
}
}

if key.Compare(n.key) < 0 {
n.left = n.left.add(key, value)
} else if key.Compare(n.key) > 0 {
n.right = n.right.add(key, value)
} else {
n.value = value
}
return n.rebalanceTree()
}

func (n *node[K, V]) remove(key K) *node[K, V] {
if n == nil {
return nil
}
if key.Compare(n.key) < 0 {
n.left = n.left.remove(key)
} else if key.Compare(n.key) > 0 {
n.right = n.right.remove(key)
} else {
if n.left != nil && n.right != nil {
rightMinNode := n.right.findSmallest()
n.key = rightMinNode.key
n.value = rightMinNode.value
n.right = n.right.remove(rightMinNode.key)
} else if n.left != nil {
n = n.left
} else if n.right != nil {
n = n.right
} else {
n = nil
return n
}

}
return n.rebalanceTree()
}

func (n *node[K, V]) search(key K) *node[K, V] {
if n == nil {
return nil
}
if key.Compare(n.key) < 0 {
return n.left.search(key)
} else if key.Compare(n.key) > 0 {
return n.right.search(key)
} else {
return n
}
}

func (n *node[K, V]) getHeight() int {
if n == nil {
return 0
}
return n.height
}

func (n *node[K, V]) recalculateHeight() {
n.height = 1 + max(n.left.getHeight(), n.right.getHeight())
}

func (n *node[K, V]) rebalanceTree() *node[K, V] {
if n == nil {
return n
}
n.recalculateHeight()

balanceFactor := n.left.getHeight() - n.right.getHeight()
if balanceFactor <= -2 {
if n.right.left.getHeight() > n.right.right.getHeight() {
n.right = n.right.rotateRight()
}
return n.rotateLeft()
} else if balanceFactor >= 2 {
if n.left.right.getHeight() > n.left.left.getHeight() {
n.left = n.left.rotateLeft()
}
return n.rotateRight()
}
return n
}

func (n *node[K, V]) rotateLeft() *node[K, V] {
newRoot := n.right
n.right = newRoot.left
newRoot.left = n

n.recalculateHeight()
newRoot.recalculateHeight()
return newRoot
}

func (n *node[K, V]) rotateRight() *node[K, V] {
newRoot := n.left
n.left = newRoot.right
newRoot.right = n

n.recalculateHeight()
newRoot.recalculateHeight()
return newRoot
}

func (n *node[K, V]) findSmallest() *node[K, V] {
if n.left != nil {
return n.left.findSmallest()
} else {
return n
}
}

func max(a int, b int) int {
if a > b {
return a
}
return b
}
83 changes: 83 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package memo

import "github.com/zyedidia/generic/list"

// A Cache is an LRU cache for keys and values. Each entry is
// put into the table with an associated key used for looking up the entry.
// The cache has a maximum size, and uses a least-recently-used eviction
// policy when there is not space for a new entry.
type Cache[K comparable, V any] struct {
size int
capacity int
lru list.List[kv[K, V]]
table map[K]*list.Node[kv[K, V]]
}

type kv[K comparable, V any] struct {
key K
val V
}

// NewTable returns a new memo.Cache with the given capacity.
func NewTable[K comparable, V any](capacity int) *Cache[K, V] {
return &Cache[K, V]{
size: 0,
capacity: capacity,
lru: list.List[kv[K, V]]{},
table: make(map[K]*list.Node[kv[K, V]]),
}
}

// Get returns the entry associated with a given key, and a boolean indicating
// whether the key exists in the table.
func (t *Cache[K, V]) Get(k K) (V, bool) {
if n, ok := t.table[k]; ok {
t.lru.Remove(n)
t.lru.Append(n)
return n.Value.val, true
}
var v V
return v, false
}

// GetZ is the same as Get but returns the zero-value if k is not found.
func (t *Cache[K, V]) GetZ(k K) V {
v, _ := t.Get(k)
return v
}

// Put adds a new key-entry pair to the table.
func (t *Cache[K, V]) Put(k K, e V) {
if n, ok := t.table[k]; ok {
n.Value.val = e
t.lru.Remove(n)
t.lru.Append(n)
return
}

if t.size == t.capacity {
key := t.lru.Tail.Value.key
t.lru.Remove(t.lru.Tail)
t.size--
delete(t.table, key)
}
n := &list.Node[kv[K, V]]{
Value: kv[K, V]{
key: k,
val: e,
},
}
t.lru.Append(n)
t.size++
t.table[k] = n
}

// Delete causes the entry associated with the given key to be immediately
// evicted from the table.
func (t *Cache[K, V]) Delete(k K) {
if n, ok := t.table[k]; ok {
t.lru.Remove(n)
t.size--
delete(t.table, k)
}
}
5 changes: 5 additions & 0 deletions constraints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package generic

type Orderable[T any] interface {
Compare(other T) int
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/zyedidia/generic

go 1.18
12 changes: 12 additions & 0 deletions hashmap/key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package hashmap

type Uint64 uint64

func (u Uint64) Hash() uint64 {
u ^= u >> 33
u *= 0xff51afd7ed558ccd
u ^= u >> 33
u *= 0xc4ceb9fe1a85ec53
u ^= u >> 33
return uint64(u)
}
Loading

0 comments on commit 997beff

Please sign in to comment.