forked from zyedidia/generic
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 997beff
Showing
12 changed files
with
829 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
todo.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/zyedidia/generic | ||
|
||
go 1.18 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.