Skip to content

Commit 6e4ec1a

Browse files
authored
Merge pull request #4 from mikenye/implement_floor_ceiling
Implement Floor & Ceiling methods, improve code clarity & improve comments.
2 parents 9e6aa08 + 42c5283 commit 6e4ec1a

File tree

6 files changed

+492
-17
lines changed

6 files changed

+492
-17
lines changed

bst/examples_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,101 @@ func ExampleTree_Predecessor_traversal() {
195195
// Node with key 1 has value one (and color: ⬛)
196196
// Node with key 0 has value zero (and color: ⬛)
197197
}
198+
199+
func ExampleTree_Floor() {
200+
// Create a tree with even numbers
201+
tree := bst.New[int, string, struct{}](func(a, b int) bool {
202+
return a < b
203+
})
204+
205+
tree.Insert(2, "two")
206+
tree.Insert(4, "four")
207+
tree.Insert(6, "six")
208+
tree.Insert(8, "eight")
209+
tree.Insert(10, "ten")
210+
211+
// Find floor value for various inputs
212+
213+
// Exact match
214+
if node, found := tree.Floor(6); found {
215+
fmt.Printf("Floor(6) = %d: %s\n", tree.Key(node), tree.Value(node))
216+
} else {
217+
fmt.Println("Floor(6) not found")
218+
}
219+
220+
// Floor value for an odd number (finds the next lower value)
221+
if node, found := tree.Floor(7); found {
222+
fmt.Printf("Floor(7) = %d: %s\n", tree.Key(node), tree.Value(node))
223+
} else {
224+
fmt.Println("Floor(7) not found")
225+
}
226+
227+
// Floor value for a value smaller than the minimum
228+
if node, found := tree.Floor(1); found {
229+
fmt.Printf("Floor(1) = %d: %s\n", tree.Key(node), tree.Value(node))
230+
} else {
231+
fmt.Println("Floor(1) not found")
232+
}
233+
234+
// Floor value for a value larger than the maximum
235+
if node, found := tree.Floor(12); found {
236+
fmt.Printf("Floor(12) = %d: %s\n", tree.Key(node), tree.Value(node))
237+
} else {
238+
fmt.Println("Floor(12) not found")
239+
}
240+
241+
// Output:
242+
// Floor(6) = 6: six
243+
// Floor(7) = 6: six
244+
// Floor(1) not found
245+
// Floor(12) = 10: ten
246+
}
247+
248+
func ExampleTree_Ceiling() {
249+
// Create a tree with even numbers
250+
tree := bst.New[int, string, struct{}](func(a, b int) bool {
251+
return a < b
252+
})
253+
254+
tree.Insert(2, "two")
255+
tree.Insert(4, "four")
256+
tree.Insert(6, "six")
257+
tree.Insert(8, "eight")
258+
tree.Insert(10, "ten")
259+
260+
// Find ceiling value for various inputs
261+
262+
// Exact match
263+
if node, found := tree.Ceiling(6); found {
264+
fmt.Printf("Ceiling(6) = %d: %s\n", tree.Key(node), tree.Value(node))
265+
} else {
266+
fmt.Println("Ceiling(6) not found")
267+
}
268+
269+
// Ceiling value for an odd number (finds the next higher value)
270+
if node, found := tree.Ceiling(5); found {
271+
fmt.Printf("Ceiling(5) = %d: %s\n", tree.Key(node), tree.Value(node))
272+
} else {
273+
fmt.Println("Ceiling(5) not found")
274+
}
275+
276+
// Ceiling value for a value smaller than the minimum
277+
if node, found := tree.Ceiling(1); found {
278+
fmt.Printf("Ceiling(1) = %d: %s\n", tree.Key(node), tree.Value(node))
279+
} else {
280+
fmt.Println("Ceiling(1) not found")
281+
}
282+
283+
// Ceiling value for a value larger than the maximum
284+
if node, found := tree.Ceiling(12); found {
285+
fmt.Printf("Ceiling(12) = %d: %s\n", tree.Key(node), tree.Value(node))
286+
} else {
287+
fmt.Println("Ceiling(12) not found")
288+
}
289+
290+
// Output:
291+
// Ceiling(6) = 6: six
292+
// Ceiling(5) = 6: six
293+
// Ceiling(1) = 2: two
294+
// Ceiling(12) not found
295+
}

bst/tree.go

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (t *Tree[K, V, M]) Insert(key K, value V) (*Node[K, V, M], bool) {
239239
// update trailing pointer
240240
parent = currNode
241241

242-
if !t.less(currNode.key, key) && !t.less(key, currNode.key) {
242+
if t.keysEqual(currNode.key, key) {
243243

244244
// If key already exists, update the value
245245
currNode.value = value
@@ -582,7 +582,7 @@ func (t *Tree[K, V, M]) Search(key K) (*Node[K, V, M], bool) {
582582
for currNode != t.nil {
583583

584584
// if we've found the matching node, return it
585-
if !t.less(currNode.key, key) && !t.less(key, currNode.key) {
585+
if t.keysEqual(currNode.key, key) {
586586
return currNode, true
587587
}
588588

@@ -876,3 +876,88 @@ func (t *Tree[K, V, M]) TraverseInOrder(n *Node[K, V, M], f TraversalFunc[K, V,
876876
func (t *Tree[K, V, M]) Value(n *Node[K, V, M]) V {
877877
return n.value
878878
}
879+
880+
// keysEqual determines if two keys are equal by using the less function.
881+
//
882+
// Two keys are considered equal if neither is less than the other.
883+
//
884+
// This is a helper method that improves performance by avoiding duplicate calls to the less function.
885+
func (t *Tree[K, V, M]) keysEqual(a, b K) bool {
886+
return !t.less(a, b) && !t.less(b, a)
887+
}
888+
889+
// Floor finds the largest key in the tree less than or equal to key.
890+
//
891+
// Returns:
892+
// - (*Node[K, V, M], true) if a key ≤ key exists in the tree.
893+
// - (nil, false) if no such key exists.
894+
func (t *Tree[K, V, M]) Floor(key K) (*Node[K, V, M], bool) {
895+
if t.IsNil(t.root) {
896+
return t.nil, false
897+
}
898+
899+
var floor *Node[K, V, M] = t.nil
900+
current := t.root
901+
902+
for !t.IsNil(current) {
903+
// If current key equals the search key, we found an exact match
904+
if t.keysEqual(current.key, key) {
905+
return current, true
906+
}
907+
908+
// If current key is greater than search key, go left
909+
if t.less(key, current.key) {
910+
current = current.left
911+
} else {
912+
// If current key is less than search key, this is a potential floor value
913+
// Update floor and continue searching in the right subtree for larger values
914+
floor = current
915+
current = current.right
916+
}
917+
}
918+
919+
// If we found a floor value, return it
920+
if !t.IsNil(floor) {
921+
return floor, true
922+
}
923+
924+
return t.nil, false
925+
}
926+
927+
// Ceiling finds the smallest key in the tree greater than or equal to key.
928+
//
929+
// Returns:
930+
// - (*Node[K, V, M], true) if a key ≥ key exists in the tree.
931+
// - (nil, false) if no such key exists.
932+
func (t *Tree[K, V, M]) Ceiling(key K) (*Node[K, V, M], bool) {
933+
if t.IsNil(t.root) {
934+
return t.nil, false
935+
}
936+
937+
var ceiling *Node[K, V, M] = t.nil
938+
current := t.root
939+
940+
for !t.IsNil(current) {
941+
// If current key equals the search key, we found an exact match
942+
if t.keysEqual(current.key, key) {
943+
return current, true
944+
}
945+
946+
// If current key is less than search key, go right
947+
if t.less(current.key, key) {
948+
current = current.right
949+
} else {
950+
// If current key is greater than search key, this is a potential ceiling value
951+
// Update ceiling and continue searching in the left subtree for smaller values
952+
ceiling = current
953+
current = current.left
954+
}
955+
}
956+
957+
// If we found a ceiling value, return it
958+
if !t.IsNil(ceiling) {
959+
return ceiling, true
960+
}
961+
962+
return t.nil, false
963+
}

bst/tree_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,3 +856,123 @@ func TestTree_Contains(t *testing.T) {
856856
assert.False(t, treeA.Contains(nB), "node from tree B should not exist in node A")
857857
assert.True(t, treeB.Contains(nB), "expected to find node B in tree B")
858858
}
859+
860+
func TestTree_Floor(t *testing.T) {
861+
tree := New[int, string, struct{}](func(a, b int) bool {
862+
return a < b
863+
})
864+
865+
// Test with empty tree
866+
n, found := tree.Floor(5)
867+
assert.False(t, found, "Floor in empty tree should return not found")
868+
assert.True(t, tree.IsNil(n), "Floor in empty tree should return nil node")
869+
870+
// Insert some values
871+
tree.Insert(10, "ten")
872+
tree.Insert(5, "five")
873+
tree.Insert(15, "fifteen")
874+
tree.Insert(3, "three")
875+
tree.Insert(7, "seven")
876+
tree.Insert(12, "twelve")
877+
tree.Insert(17, "seventeen")
878+
879+
// Test exact matches
880+
n, found = tree.Floor(10)
881+
assert.True(t, found, "Floor for existing key should be found")
882+
assert.Equal(t, 10, tree.Key(n), "Floor(10) should return node with key 10")
883+
assert.Equal(t, "ten", tree.Value(n), "Floor(10) should return node with value 'ten'")
884+
885+
// Test where floor is less than key
886+
n, found = tree.Floor(6)
887+
assert.True(t, found, "Floor(6) should find a node")
888+
assert.Equal(t, 5, tree.Key(n), "Floor(6) should return node with key 5")
889+
assert.Equal(t, "five", tree.Value(n), "Floor(6) should return node with value 'five'")
890+
891+
// Test where floor is less than smallest key
892+
n, found = tree.Floor(2)
893+
assert.False(t, found, "Floor(2) should not find a node")
894+
assert.True(t, tree.IsNil(n), "Floor(2) should return nil node")
895+
896+
// Test where floor is largest key
897+
n, found = tree.Floor(20)
898+
assert.True(t, found, "Floor(20) should find a node")
899+
assert.Equal(t, 17, tree.Key(n), "Floor(20) should return node with key 17")
900+
assert.Equal(t, "seventeen", tree.Value(n), "Floor(20) should return node with value 'seventeen'")
901+
}
902+
903+
func TestTree_Ceiling(t *testing.T) {
904+
tree := New[int, string, struct{}](func(a, b int) bool {
905+
return a < b
906+
})
907+
908+
// Test with empty tree
909+
n, found := tree.Ceiling(5)
910+
assert.False(t, found, "Ceiling in empty tree should return not found")
911+
assert.True(t, tree.IsNil(n), "Ceiling in empty tree should return nil node")
912+
913+
// Insert some values
914+
tree.Insert(10, "ten")
915+
tree.Insert(5, "five")
916+
tree.Insert(15, "fifteen")
917+
tree.Insert(3, "three")
918+
tree.Insert(7, "seven")
919+
tree.Insert(12, "twelve")
920+
tree.Insert(17, "seventeen")
921+
922+
// Test exact matches
923+
n, found = tree.Ceiling(10)
924+
assert.True(t, found, "Ceiling for existing key should be found")
925+
assert.Equal(t, 10, tree.Key(n), "Ceiling(10) should return node with key 10")
926+
assert.Equal(t, "ten", tree.Value(n), "Ceiling(10) should return node with value 'ten'")
927+
928+
// Test where ceiling is greater than key
929+
n, found = tree.Ceiling(6)
930+
assert.True(t, found, "Ceiling(6) should find a node")
931+
assert.Equal(t, 7, tree.Key(n), "Ceiling(6) should return node with key 7")
932+
assert.Equal(t, "seven", tree.Value(n), "Ceiling(6) should return node with value 'seven'")
933+
934+
// Test where ceiling is smallest key
935+
n, found = tree.Ceiling(1)
936+
assert.True(t, found, "Ceiling(1) should find a node")
937+
assert.Equal(t, 3, tree.Key(n), "Ceiling(1) should return node with key 3")
938+
assert.Equal(t, "three", tree.Value(n), "Ceiling(1) should return node with value 'three'")
939+
940+
// Test where key is larger than largest in tree
941+
n, found = tree.Ceiling(20)
942+
assert.False(t, found, "Ceiling(20) should not find a node")
943+
assert.True(t, tree.IsNil(n), "Ceiling(20) should return nil node")
944+
}
945+
946+
// TestTree_UncoveredSetMethods tests the uncovered set methods
947+
func TestTree_UncoveredSetMethods(t *testing.T) {
948+
tree := New[int, string, struct{}](func(a, b int) bool {
949+
return a < b
950+
})
951+
952+
// Create a test node
953+
node, _ := tree.Insert(10, "original value")
954+
955+
// Test SetValue
956+
tree.SetValue(node, "new value")
957+
assert.Equal(t, "new value", tree.Value(node), "SetValue should update the node's value")
958+
959+
// Test SetLeft and SetRight
960+
leftNode, _ := tree.Insert(5, "left")
961+
rightNode, _ := tree.Insert(15, "right")
962+
963+
// Save original relationships
964+
originalLeft := node.left
965+
originalRight := node.right
966+
967+
// Manually change the relationships with SetLeft and SetRight
968+
tree.SetLeft(node, rightNode) // Intentionally incorrect for testing
969+
tree.SetRight(node, leftNode) // Intentionally incorrect for testing
970+
971+
// Verify the changes took effect
972+
assert.Equal(t, rightNode, tree.Left(node), "SetLeft should update the node's left child")
973+
assert.Equal(t, leftNode, tree.Right(node), "SetRight should update the node's right child")
974+
975+
// Restore original structure to avoid affecting other tests
976+
tree.SetLeft(node, originalLeft)
977+
tree.SetRight(node, originalRight)
978+
}

0 commit comments

Comments
 (0)