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