binary search tree
This commit is contained in:
		
							parent
							
								
									d4cbad40c3
								
							
						
					
					
						commit
						fcd2f9b99a
					
				
					 3 changed files with 743 additions and 0 deletions
				
			
		
							
								
								
									
										389
									
								
								searching/binary_search_tree.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										389
									
								
								searching/binary_search_tree.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,389 @@ | |||
| package searching | ||||
| 
 | ||||
| import "github.com/fotonmoton/algorithms/fundamentals/queue" | ||||
| 
 | ||||
| type bstNode[K any, V any] struct { | ||||
| 	left  *bstNode[K, V] | ||||
| 	right *bstNode[K, V] | ||||
| 	key   K | ||||
| 	val   V | ||||
| 	n     int64 | ||||
| } | ||||
| 
 | ||||
| // TODO: maybe pass pointers for recursive funcs? | ||||
| type bst[K any, V any] struct { | ||||
| 	root *bstNode[K, V] | ||||
| 	cmp  func(*K, *K) int | ||||
| } | ||||
| 
 | ||||
| func NewBST[K any, V any](cmp func(*K, *K) int) SymbolTable[K, V] { | ||||
| 	return &bst[K, V]{nil, cmp} | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Put(key K, val V) { | ||||
| 	t.root = t.put(key, val, t.root) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) put(key K, val V, node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node == nil { | ||||
| 		return &bstNode[K, V]{nil, nil, key, val, 1} | ||||
| 	} | ||||
| 
 | ||||
| 	cmp := t.cmp(&key, &node.key) | ||||
| 
 | ||||
| 	if cmp < 0 { | ||||
| 		node.left = t.put(key, val, node.left) | ||||
| 	} | ||||
| 
 | ||||
| 	if cmp == 0 { | ||||
| 		node.val = val | ||||
| 	} | ||||
| 
 | ||||
| 	if cmp > 0 { | ||||
| 		node.right = t.put(key, val, node.right) | ||||
| 	} | ||||
| 
 | ||||
| 	node.n = t.size(node.left) + t.size(node.right) + 1 | ||||
| 	return node | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Get(key K) *V { | ||||
| 	return t.get(key, t.root) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) get(key K, node *bstNode[K, V]) *V { | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	cmp := t.cmp(&key, &node.key) | ||||
| 
 | ||||
| 	if cmp < 0 { | ||||
| 		return t.get(key, node.left) | ||||
| 	} | ||||
| 
 | ||||
| 	if cmp > 0 { | ||||
| 		return t.get(key, node.right) | ||||
| 	} | ||||
| 
 | ||||
| 	return &node.val | ||||
| } | ||||
| 
 | ||||
| func (t *bst[_, __]) Size() int64 { | ||||
| 	return t.size(t.root) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) size(node *bstNode[K, V]) int64 { | ||||
| 	if node == nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 
 | ||||
| 	return node.n | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, _]) Min() *K { | ||||
| 	if t.root == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return &t.min(t.root).key | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) min(node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node.left == nil { | ||||
| 		return node | ||||
| 	} | ||||
| 
 | ||||
| 	return t.min(node.left) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, _]) Max() *K { | ||||
| 	if t.root == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return &t.max(t.root).key | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) max(node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node.right == nil { | ||||
| 		return node | ||||
| 	} | ||||
| 
 | ||||
| 	return t.max(node.right) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Floor(key K) *K { | ||||
| 	largest := t.floor(key, t.root) | ||||
| 
 | ||||
| 	if largest == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return &largest.key | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) floor(key K, node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	cmp := t.cmp(&key, &node.key) | ||||
| 
 | ||||
| 	if cmp == 0 { | ||||
| 		return node | ||||
| 	} | ||||
| 
 | ||||
| 	if cmp < 0 { | ||||
| 		return t.floor(key, node.left) | ||||
| 	} | ||||
| 
 | ||||
| 	larger := t.floor(key, node.right) | ||||
| 
 | ||||
| 	if larger != nil { | ||||
| 		return larger | ||||
| 	} | ||||
| 
 | ||||
| 	return node | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Ceiling(key K) *K { | ||||
| 	smallest := t.ceiling(key, t.root) | ||||
| 
 | ||||
| 	if smallest == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return &smallest.key | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) ceiling(key K, node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	cmp := t.cmp(&key, &node.key) | ||||
| 
 | ||||
| 	if cmp == 0 { | ||||
| 		return node | ||||
| 	} | ||||
| 
 | ||||
| 	if cmp > 0 { | ||||
| 		return t.ceiling(key, node.right) | ||||
| 	} | ||||
| 
 | ||||
| 	smaller := t.ceiling(key, node.left) | ||||
| 
 | ||||
| 	if smaller != nil { | ||||
| 		return smaller | ||||
| 	} | ||||
| 
 | ||||
| 	return node | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Rank(key K) int64 { | ||||
| 	return t.rank(key, t.root) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) rank(key K, node *bstNode[K, V]) int64 { | ||||
| 	if node == nil { | ||||
| 		return 0 | ||||
| 	} | ||||
| 
 | ||||
| 	cmp := t.cmp(&key, &node.key) | ||||
| 
 | ||||
| 	// If we found key in a tree then left subtree | ||||
| 	// will always contain keys less than current node key | ||||
| 	// and right subtree will always ontain greater keys (by BST definition). | ||||
| 	// So we simply return left subtree size | ||||
| 	if cmp == 0 { | ||||
| 		return t.size(node.left) | ||||
| 	} | ||||
| 
 | ||||
| 	// If current node key is bigger than key for which rank is searched | ||||
| 	// we should descend deeper in left subtree | ||||
| 	if cmp < 0 { | ||||
| 		return t.rank(key, node.left) | ||||
| 	} | ||||
| 
 | ||||
| 	// If we found node with key that is less than search key | ||||
| 	// we get the size of the left subtree, add 1 to count current node in | ||||
| 	// rank value and descend deeper in right subtree. | ||||
| 	return 1 + t.size(node.left) + t.rank(key, node.right) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) KeyByRank(i int64) *K { | ||||
| 	node := t.keyByRank(i, t.root) | ||||
| 
 | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	return &node.key | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) keyByRank(rank int64, node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// We need left subtree size to substract it from our index | ||||
| 	// when we descend deeper in right subtree | ||||
| 	leftSize := t.size(node.left) | ||||
| 
 | ||||
| 	if rank < leftSize { | ||||
| 		return t.keyByRank(rank, node.left) | ||||
| 	} | ||||
| 
 | ||||
| 	if rank > leftSize { | ||||
| 		// We subtract left size subtree | ||||
| 		return t.keyByRank(rank-leftSize-1, node.right) | ||||
| 	} | ||||
| 
 | ||||
| 	return node | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Contains(key K) bool { | ||||
| 	return t.Get(key) == nil | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) IsEmpty() bool { | ||||
| 	return t.Size() == 0 | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) DeleteMin() { | ||||
| 
 | ||||
| 	if t.root == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	t.root = t.deleteMin(t.root) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) deleteMin(node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node.left == nil { | ||||
| 		return node.right | ||||
| 	} | ||||
| 
 | ||||
| 	node.left = t.deleteMin(node.left) | ||||
| 	node.n = t.size(node.left) + t.size(node.right) + 1 | ||||
| 
 | ||||
| 	return node | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) DeleteMax() { | ||||
| 
 | ||||
| 	if t.root == nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	t.root = t.deleteMax(t.root) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) deleteMax(node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node.right == nil { | ||||
| 		return node.left | ||||
| 	} | ||||
| 
 | ||||
| 	node.right = t.deleteMax(node.right) | ||||
| 	node.n = t.size(node.left) + t.size(node.right) + 1 | ||||
| 
 | ||||
| 	return node | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Delete(key K) { | ||||
| 	t.root = t.delete(key, t.root) | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) delete(key K, node *bstNode[K, V]) *bstNode[K, V] { | ||||
| 	if node == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	cmp := t.cmp(&key, &node.key) | ||||
| 
 | ||||
| 	if cmp < 0 { | ||||
| 		node.left = t.delete(key, node.left) | ||||
| 	} else if cmp > 0 { | ||||
| 		node.right = t.delete(key, node.right) | ||||
| 	} else { | ||||
| 
 | ||||
| 		// Shortcut: we can return left or right subtree if we have only one of them | ||||
| 		// without size recalculation and pointers juggling | ||||
| 		if node.right == nil { | ||||
| 			return node.left | ||||
| 		} | ||||
| 		if node.left == nil { | ||||
| 			return node.right | ||||
| 		} | ||||
| 
 | ||||
| 		// Needed to delete "min" node in right subtree | ||||
| 		tmp := node | ||||
| 		// We substitute current node with "min" node from right subtree. | ||||
| 		// When "node" variable will be returned to the caller "tmp" node | ||||
| 		// will be erased by "node" value and be marked for garbage collection. | ||||
| 		// At least it should work as described | ||||
| 		node = t.min(tmp.right) | ||||
| 		// We prevent "node" duplication in the tree by deleting it from right subtree | ||||
| 		node.right = t.deleteMin(tmp.right) | ||||
| 		// Left subtree stays unchanged | ||||
| 		node.left = tmp.left | ||||
| 	} | ||||
| 	node.n = t.size(node.left) + t.size(node.right) + 1 | ||||
| 	return node | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) KeysBetween(lo, hi K) []K { | ||||
| 	q := queue.NewQueue[K]() | ||||
| 	t.keysBetween(lo, hi, t.root, q) | ||||
| 	keys := make([]K, 0, q.Size()) | ||||
| 
 | ||||
| 	for !q.IsEmpty() { | ||||
| 		keys = append(keys, q.Dequeue()) | ||||
| 	} | ||||
| 
 | ||||
| 	return keys | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) keysBetween(lo, hi K, node *bstNode[K, V], q queue.Queue[K]) { | ||||
| 	if node == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	cmplo := t.cmp(&lo, &node.key) | ||||
| 	cmphi := t.cmp(&hi, &node.key) | ||||
| 
 | ||||
| 	if cmplo < 0 { | ||||
| 		t.keysBetween(lo, hi, node.left, q) | ||||
| 	} | ||||
| 
 | ||||
| 	if cmplo <= 0 && cmphi >= 0 { | ||||
| 		q.Enqueue(node.key) | ||||
| 	} | ||||
| 
 | ||||
| 	if cmphi > 0 { | ||||
| 		t.keysBetween(lo, hi, node.right, q) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) Keys() []K { | ||||
| 	if t.IsEmpty() { | ||||
| 		return []K{} | ||||
| 	} | ||||
| 
 | ||||
| 	q := queue.NewQueue[K]() | ||||
| 	t.keysBetween(*t.Min(), *t.Max(), t.root, q) | ||||
| 	keys := make([]K, 0, q.Size()) | ||||
| 
 | ||||
| 	for !q.IsEmpty() { | ||||
| 		keys = append(keys, q.Dequeue()) | ||||
| 	} | ||||
| 
 | ||||
| 	return keys | ||||
| } | ||||
| 
 | ||||
| func (t *bst[K, V]) SizeBetween(lo K, hi K) int64 { | ||||
| 	q := queue.NewQueue[K]() | ||||
| 	t.keysBetween(lo, hi, t.root, q) | ||||
| 
 | ||||
| 	return int64(q.Size()) | ||||
| } | ||||
							
								
								
									
										331
									
								
								searching/binary_search_tree_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								searching/binary_search_tree_test.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,331 @@ | |||
| package searching | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| func intCompare(a, b *int) int { | ||||
| 	if *a < *b { | ||||
| 		return -1 | ||||
| 	} | ||||
| 
 | ||||
| 	if *a > *b { | ||||
| 		return 1 | ||||
| 	} | ||||
| 
 | ||||
| 	return 0 | ||||
| } | ||||
| 
 | ||||
| func TestPut(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	table.Put(1, 10) | ||||
| 	table.Put(2, 20) | ||||
| 
 | ||||
| 	assert.Equal(t, 10, *table.Get(1)) | ||||
| 	assert.Equal(t, 20, *table.Get(2)) | ||||
| 
 | ||||
| 	// rewrite | ||||
| 	table.Put(1, 11) | ||||
| 
 | ||||
| 	assert.Equal(t, 11, *table.Get(1)) | ||||
| 	assert.Equal(t, 20, *table.Get(2)) | ||||
| 	assert.Equal(t, int64(2), table.Size()) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestGet(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Nil(t, table.Get(0)) | ||||
| 
 | ||||
| 	table.Put(1, 2) | ||||
| 
 | ||||
| 	assert.Equal(t, 2, *table.Get(1)) | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestSize(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Equal(t, int64(0), table.Size()) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 
 | ||||
| 	assert.Equal(t, int64(1), table.Size()) | ||||
| 
 | ||||
| 	table.Put(2, 2) | ||||
| 
 | ||||
| 	assert.Equal(t, int64(2), table.Size()) | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestMin(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Nil(t, table.Min()) | ||||
| 
 | ||||
| 	table.Put(3, 3) | ||||
| 
 | ||||
| 	assert.Equal(t, 3, *table.Min()) | ||||
| 
 | ||||
| 	table.Put(2, 2) | ||||
| 
 | ||||
| 	assert.Equal(t, 2, *table.Min()) | ||||
| 
 | ||||
| 	table.Put(4, 4) | ||||
| 
 | ||||
| 	assert.Equal(t, 2, *table.Min()) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 
 | ||||
| 	assert.Equal(t, 1, *table.Min()) | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestMax(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Nil(t, table.Max()) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 	assert.Equal(t, 1, *table.Max()) | ||||
| 
 | ||||
| 	table.Put(2, 2) | ||||
| 	assert.Equal(t, 2, *table.Max()) | ||||
| 
 | ||||
| 	table.Put(5, 5) | ||||
| 	assert.Equal(t, 5, *table.Max()) | ||||
| 
 | ||||
| 	table.Put(4, 4) | ||||
| 	assert.Equal(t, 5, *table.Max()) | ||||
| 
 | ||||
| 	table.Put(3, 3) | ||||
| 	assert.Equal(t, 5, *table.Max()) | ||||
| 
 | ||||
| 	table.Put(5, 55) | ||||
| 	assert.Equal(t, 5, *table.Max()) | ||||
| 
 | ||||
| 	table.Put(6, 6) | ||||
| 	assert.Equal(t, 6, *table.Max()) | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestFloor(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Nil(t, table.Floor(0)) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 	assert.Equal(t, 1, *table.Floor(1)) | ||||
| 
 | ||||
| 	table.Put(5, 5) | ||||
| 	assert.Equal(t, 5, *table.Floor(5)) | ||||
| 	assert.Equal(t, 1, *table.Floor(4)) | ||||
| 
 | ||||
| 	table.Put(4, 4) | ||||
| 	assert.Equal(t, 5, *table.Floor(5)) | ||||
| 	assert.Equal(t, 4, *table.Floor(4)) | ||||
| 	assert.Equal(t, 1, *table.Floor(3)) | ||||
| 	assert.Nil(t, table.Floor(0)) | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestCeiling(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Nil(t, table.Ceiling(0)) | ||||
| 
 | ||||
| 	table.Put(5, 5) | ||||
| 	assert.Equal(t, 5, *table.Ceiling(5)) | ||||
| 
 | ||||
| 	table.Put(4, 4) | ||||
| 	assert.Equal(t, 4, *table.Ceiling(0)) | ||||
| 	assert.Equal(t, 5, *table.Ceiling(5)) | ||||
| 
 | ||||
| 	table.Put(3, 3) | ||||
| 	assert.Equal(t, 3, *table.Ceiling(0)) | ||||
| 	assert.Equal(t, 3, *table.Ceiling(1)) | ||||
| 	assert.Equal(t, 3, *table.Ceiling(3)) | ||||
| 	assert.Equal(t, 4, *table.Ceiling(4)) | ||||
| 	assert.Equal(t, 5, *table.Ceiling(5)) | ||||
| 	assert.Nil(t, table.Ceiling(6)) | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestRank(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Equal(t, int64(0), table.Rank(1)) | ||||
| 
 | ||||
| 	table.Put(0, 0) | ||||
| 	assert.Equal(t, int64(1), table.Rank(1)) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 	assert.Equal(t, int64(2), table.Rank(2)) | ||||
| 
 | ||||
| 	table.Put(4, 4) | ||||
| 	assert.Equal(t, int64(2), table.Rank(2)) | ||||
| 	assert.Equal(t, int64(2), table.Rank(3)) | ||||
| 	assert.Equal(t, int64(3), table.Rank(5)) | ||||
| 
 | ||||
| 	table.Put(2, 2) | ||||
| 	assert.Equal(t, int64(2), table.Rank(2)) | ||||
| 	assert.Equal(t, int64(3), table.Rank(3)) | ||||
| 	assert.Equal(t, int64(4), table.Rank(5)) | ||||
| 
 | ||||
| 	table.Put(3, 3) | ||||
| 	assert.Equal(t, int64(2), table.Rank(2)) | ||||
| 	assert.Equal(t, int64(3), table.Rank(3)) | ||||
| 	assert.Equal(t, int64(4), table.Rank(4)) | ||||
| 	assert.Equal(t, int64(5), table.Rank(5)) | ||||
| } | ||||
| 
 | ||||
| // TODO: test with delete | ||||
| func TestKeyByRank(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.Nil(t, table.KeyByRank(1)) | ||||
| 
 | ||||
| 	table.Put(0, 0) | ||||
| 	assert.Nil(t, table.KeyByRank(1)) | ||||
| 	assert.Equal(t, 0, *table.KeyByRank(table.Rank(0))) | ||||
| 
 | ||||
| 	table.Put(5, 5) | ||||
| 	assert.Equal(t, 5, *table.KeyByRank(table.Rank(5))) | ||||
| 	assert.EqualValues(t, 1, table.Rank(*table.KeyByRank(1))) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteMin(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	table.DeleteMin() | ||||
| 
 | ||||
| 	table.Put(0, 0) | ||||
| 	assert.EqualValues(t, 1, table.Size()) | ||||
| 
 | ||||
| 	table.DeleteMin() | ||||
| 	assert.EqualValues(t, 0, table.Size()) | ||||
| 
 | ||||
| 	table.Put(5, 5) | ||||
| 	table.Put(0, 0) | ||||
| 	table.Put(1, 1) | ||||
| 	table.Put(2, 2) | ||||
| 
 | ||||
| 	assert.Equal(t, 0, *table.Get(0)) | ||||
| 
 | ||||
| 	table.DeleteMin() | ||||
| 
 | ||||
| 	assert.Nil(t, table.Get(0)) | ||||
| 	assert.EqualValues(t, 3, table.Size()) | ||||
| } | ||||
| 
 | ||||
| func TestDeleteMax(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	table.DeleteMin() | ||||
| 
 | ||||
| 	table.Put(0, 0) | ||||
| 	assert.EqualValues(t, 1, table.Size()) | ||||
| 
 | ||||
| 	table.DeleteMax() | ||||
| 	assert.EqualValues(t, 0, table.Size()) | ||||
| 
 | ||||
| 	table.Put(0, 0) | ||||
| 	table.Put(5, 5) | ||||
| 	table.Put(1, 1) | ||||
| 	table.Put(2, 2) | ||||
| 
 | ||||
| 	assert.Equal(t, 5, *table.Get(5)) | ||||
| 
 | ||||
| 	table.DeleteMax() | ||||
| 
 | ||||
| 	assert.Nil(t, table.Get(5)) | ||||
| 	assert.EqualValues(t, 3, table.Size()) | ||||
| } | ||||
| 
 | ||||
| // TODO: add more cases | ||||
| func TestDelete(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	table.Delete(0) | ||||
| 
 | ||||
| 	table.Put(0, 0) | ||||
| 
 | ||||
| 	table.Delete(0) | ||||
| 	assert.EqualValues(t, 0, table.Size()) | ||||
| 	assert.Nil(t, table.Get(0)) | ||||
| 
 | ||||
| 	table.Put(0, 0) | ||||
| 	table.Put(5, 5) | ||||
| 	table.Put(1, 1) | ||||
| 	table.Put(2, 2) | ||||
| 
 | ||||
| 	assert.Equal(t, 1, *table.Get(1)) | ||||
| 
 | ||||
| 	table.Delete(1) | ||||
| 	assert.Nil(t, table.Get(1)) | ||||
| 	assert.EqualValues(t, 3, table.Size()) | ||||
| 
 | ||||
| 	table.Delete(2) | ||||
| 	table.Delete(5) | ||||
| 	table.Delete(0) | ||||
| 	assert.EqualValues(t, 0, table.Size()) | ||||
| } | ||||
| 
 | ||||
| func TestKeysBetween(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.EqualValues(t, []int{}, table.KeysBetween(0, 10)) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 
 | ||||
| 	assert.EqualValues(t, []int{}, table.KeysBetween(2, 10)) | ||||
| 	assert.EqualValues(t, []int{1}, table.KeysBetween(1, 1)) | ||||
| 
 | ||||
| 	table.Put(2, 2) | ||||
| 	table.Put(5, 5) | ||||
| 
 | ||||
| 	assert.EqualValues(t, []int{5}, table.KeysBetween(3, 10)) | ||||
| 	assert.EqualValues(t, []int{1, 2, 5}, table.KeysBetween(1, 5)) | ||||
| } | ||||
| 
 | ||||
| func TestKeys(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.EqualValues(t, []int{}, table.Keys()) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 
 | ||||
| 	assert.EqualValues(t, []int{1}, table.Keys()) | ||||
| 
 | ||||
| 	table.Put(2, 2) | ||||
| 	table.Put(5, 5) | ||||
| 
 | ||||
| 	assert.EqualValues(t, []int{1, 2, 5}, table.Keys()) | ||||
| 
 | ||||
| 	table.Delete(2) | ||||
| 
 | ||||
| 	assert.EqualValues(t, []int{1, 5}, table.Keys()) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestSizeBetween(t *testing.T) { | ||||
| 	table := NewBST[int, int](intCompare) | ||||
| 
 | ||||
| 	assert.EqualValues(t, 0, table.SizeBetween(0, 10)) | ||||
| 
 | ||||
| 	table.Put(1, 1) | ||||
| 
 | ||||
| 	assert.EqualValues(t, 0, table.SizeBetween(2, 10)) | ||||
| 	assert.EqualValues(t, 1, table.SizeBetween(1, 1)) | ||||
| 
 | ||||
| 	table.Put(2, 2) | ||||
| 	table.Put(5, 5) | ||||
| 
 | ||||
| 	assert.EqualValues(t, 1, table.SizeBetween(3, 10)) | ||||
| 	assert.EqualValues(t, 3, table.SizeBetween(1, 5)) | ||||
| } | ||||
							
								
								
									
										23
									
								
								searching/symbol_table.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								searching/symbol_table.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| package searching | ||||
| 
 | ||||
| // TODO: think about pointer semantics: where pointers should be used? | ||||
| // Does go compiler silently convert values to pointers when they are leave table? | ||||
| type SymbolTable[K any, V any] interface { | ||||
| 	Put(K, V)               // add value V with associated key K to symbol table | ||||
| 	Get(K) *V               // get value V with associated key K to symbol table, nil if value is absent | ||||
| 	Size() int64            // number of key-value pairs | ||||
| 	Min() *K                // smallest key | ||||
| 	Max() *K                // largest key | ||||
| 	Floor(K) *K             // largest key less than or equal to K | ||||
| 	Ceiling(K) *K           // smallest key greater or equal to K | ||||
| 	Rank(K) int64           // number of keys less than K. Rank(*Index(in)) = in | ||||
| 	KeyByRank(int64) *K     // key of specified rank. *Index(Rank(K)) = K | ||||
| 	Contains(K) bool        // check if key K exists in symbol table | ||||
| 	IsEmpty() bool          // check if symbol table is empty | ||||
| 	DeleteMin()             // delete value with smallest key | ||||
| 	DeleteMax()             // delete value with largest key | ||||
| 	Delete(K)               // delete value associated with key K. | ||||
| 	KeysBetween(K, K) []K   // keys between two other keys in sorted order | ||||
| 	Keys() []K              // all existing keys in sorted order | ||||
| 	SizeBetween(K, K) int64 // number of keys between two keys | ||||
| } | ||||
		Loading…
	
	Add table
		
		Reference in a new issue