diff --git a/src/Containers-BinarySearchTree-Tests/CTBinarySearchTreeTest.class.st b/src/Containers-BinarySearchTree-Tests/CTBinarySearchTreeTest.class.st index 9f9112b..df28544 100644 --- a/src/Containers-BinarySearchTree-Tests/CTBinarySearchTreeTest.class.st +++ b/src/Containers-BinarySearchTree-Tests/CTBinarySearchTreeTest.class.st @@ -1,5 +1,9 @@ " -A CTBinarySearchTreeTest is a test class for testing the behavior of CTBinarySearchTree +I test the Binary Search Tree implementation (CTBinarySearchTree). + +I verify correctness of all tree operations including insertion, deletion, search, traversals, and collection protocol methods. I test edge cases like empty trees, single nodes, and complex removal scenarios. + +I ensure the BST maintains its ordering property and handles duplicates correctly. " Class { #name : 'CTBinarySearchTreeTest', @@ -20,9 +24,8 @@ CTBinarySearchTreeTest >> setUp [ { #category : 'tests' } CTBinarySearchTreeTest >> testAddMultipleElements [ - tree add: 50. - tree add: 30. - tree add: 70. + tree addAll: #(50 30 70). + self assert: tree size equals: 3 ] @@ -34,6 +37,132 @@ CTBinarySearchTreeTest >> testAddSingleElement [ self assert: tree size equals: 1 ] +{ #category : 'tests' } +CTBinarySearchTreeTest >> testAsArray [ + + + | result | + tree addAll: #(50 30 70 20 40). + + result := tree asArray. + self assert: result equals: #(20 30 40 50 70) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testBSTValidation [ + + | emptyTree | + emptyTree := CTBinarySearchTree new. + self assert: emptyTree validateBSTProperty. + + tree addAll: #(50 30 70 20 40). + self assert: tree validateBSTProperty. + + "Test after removal" + tree remove: 30. + self assert: tree validateBSTProperty +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testClear [ + + tree addAll: #(1 2 3 4 5). + tree clear. + + self assert: tree isEmpty. + self assert: tree size equals: 0 +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testCollectionMethods [ + + | doubled evens found | + tree addAll: #(50 30 70 20 40). + + + doubled := tree collect: [ :each | each * 2 ]. + self assert: doubled asArray equals: #(40 60 80 100 140). + + evens := tree select: [ :each | each even ]. + self assert: evens asArray equals: #(20 30 40 50 70). + + found := tree detect: [ :each | each > 45 ] ifNone: [ nil ]. + self assert: found equals: 50. + + self assert: (tree anySatisfy: [ :each | each > 60 ]). + self deny: (tree anySatisfy: [ :each | each > 100 ]) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testCopy [ + + | copiedTree | + tree addAll: #(50 30 70). + copiedTree := tree copy. + + self assert: copiedTree size equals: tree size. + self assert: copiedTree asArray equals: tree asArray. + + copiedTree add: 99. + self deny: (tree includes: 99). + self assert: (copiedTree includes: 99) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testDoMethod [ + + | result | + tree addAll: #(50 30 70 20 40). + + result := OrderedCollection new. + tree do: [ :each | result add: each ]. + + self assert: result asArray equals: #(20 30 40 50 70) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testDuplicateHandling [ + + tree addAll: #( 42 42 50 ). + + self assert: tree size equals: 2. + + self assert: (tree includes: 42). + self assert: (tree includes: 50) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testElementsGreaterThan [ + + | result | + tree addAll: #(50 30 70 20 40 60 80). + + result := tree elementsGreaterThan: 45. + self assert: result asArray equals: #(50 60 70 80). + + result := tree elementsGreaterThan: 80. + self assert: result isEmpty. + + result := tree elementsGreaterThan: 10. + self assert: result asArray equals: #(20 30 40 50 60 70 80) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testElementsLessThan [ + + | result | + tree addAll: #(50 30 70 20 40 60 80). + + result := tree elementsLessThan: 45. + self assert: result asArray equals: #(20 30 40). + + result := tree elementsLessThan: 20. + self assert: result isEmpty. + + result := tree elementsLessThan: 90. + self assert: result asArray equals: #(20 30 40 50 60 70 80) +] + { #category : 'tests' } CTBinarySearchTreeTest >> testEmpty [ @@ -41,6 +170,30 @@ CTBinarySearchTreeTest >> testEmpty [ self assert: tree size equals: 0 ] +{ #category : 'tests' } +CTBinarySearchTreeTest >> testEmptyTreeOperations [ + + | result | + self assert: tree isEmpty. + self assert: tree findMin isNil. + self assert: tree findMax isNil. + self deny: (tree includes: 42). + + result := tree collect: [ :each | each * 2 ]. + self assert: result isEmpty. + + self deny: (tree anySatisfy: [ :each | true ]) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testFindMinMax [ + + tree addAll: #(50 30 70 20 80). + + self assert: tree findMin equals: 20. + self assert: tree findMax equals: 80 +] + { #category : 'tests' } CTBinarySearchTreeTest >> testHeight [ @@ -49,39 +202,31 @@ CTBinarySearchTreeTest >> testHeight [ tree add: 50. self assert: tree height equals: 1. - tree add: 30. - self assert: tree height equals: 2. - - tree add: 70. + tree addAll: #(30 70). self assert: tree height equals: 2 ] { #category : 'tests' } CTBinarySearchTreeTest >> testInOrderTraversal [ - | result | - tree add: 50. - tree add: 30. - tree add: 70. - tree add: 20. - tree add: 40. - + | result | + tree addAll: #(50 30 70 20 40). + result := OrderedCollection new. tree inOrderDo: [ :each | result add: each ]. - self assert: result asArray equals: #( 20 30 40 50 70 ) + + self assert: result asArray equals: #(20 30 40 50 70) ] { #category : 'tests' } CTBinarySearchTreeTest >> testIncludes [ - tree add: 50. - tree add: 30. - tree add: 70. + tree addAll: #(50 30 70 20 40). self assert: (tree includes: 50). self assert: (tree includes: 30). - self assert: (tree includes: 70). - self deny: (tree includes: 99) + self deny: (tree includes: 99). + self deny: (tree includes: 25) ] { #category : 'tests' } @@ -94,3 +239,147 @@ CTBinarySearchTreeTest >> testIsLeaf [ self deny: tree root isLeaf. self assert: tree root left isLeaf ] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testNegativeNumbersTree [ + + | result | + tree addAll: #(-10 -5 0 5 10). + + self assert: tree findMin equals: -10. + self assert: tree findMax equals: 10. + result := tree asArray. + self assert: result equals: #(-10 -5 0 5 10) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testPostOrderTraversal [ + + | result | + tree addAll: #( 50 30 70 20 ). + + result := OrderedCollection new. + tree postOrderDo: [ :each | result add: each ]. + + self assert: result last equals: 50. + self assert: result asArray equals: #( 20 30 70 50 ) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testPreOrderTraversal [ + + | result | + tree addAll: #( 50 30 70 20 ). + + result := OrderedCollection new. + tree preOrderDo: [ :each | result add: each ]. + self assert: result first equals: 50. + self assert: result asArray equals: #(50 30 20 70) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testPredecessorSuccessorWithNonExistentElement [ + + tree addAll: #( 50 30 70 20 40 60 80 ). + + self assert: (tree predecessorOf: 35) equals: 30. + self assert: (tree successorOf: 35) equals: 40 +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testPredecessors [ + + tree addAll: #( 50 30 70 20 40 60 80 ). + + self assert: (tree predecessorOf: 50) equals: 40. + self assert: (tree predecessorOf: 30) equals: 20. + self assert: (tree predecessorOf: 20) isNil +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testRangeQuery [ + + | result temp | + tree addAll: #( 50 30 70 20 40 60 80 ). + + result := tree elementsFrom: 35 to: 65. + temp := result asArray. + self assert: result size equals: 3. + self assert: temp equals: #(40 50 60). + + "Test edge cases" + result := tree elementsFrom: 10 to: 15. + self assert: result isEmpty. + + result := tree elementsFrom: 15 to: 5. "Invalid range" + self assert: result isEmpty +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testRemoveLeafNode [ + + tree addAll: #(50 30 70 20). + + tree remove: 20. + self assert: tree size equals: 3. + self deny: (tree includes: 20) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testRemoveNodeWithOneChild [ + + tree addAll: #( 50 30 20 ). + + tree remove: 30. + self assert: tree size equals: 2. + self deny: (tree includes: 30). + self assert: (tree includes: 20) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testRemoveNodeWithTwoChildren [ + + tree addAll: #(50 30 70 20 40). + + tree remove: 30. + self assert: tree size equals: 4. + self deny: (tree includes: 30) +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testRemoveNonExistentElement [ + + | result | + + tree addAll: #(50 30 70). + result := tree remove: 99 ifAbsent: [ #notFound ]. + self assert: result equals: #notFound. + self assert: tree size equals: 3. + + self should: [ tree remove: 99 ] raise: Error +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testRemoveRoot [ + "Root with no children" + tree add: 50. + tree remove: 50. + self assert: tree isEmpty. + + "Root with two children" + tree clear. + tree addAll: #(50 30 70 20 40 60 80). + tree remove: 50. + self assert: tree size equals: 6. + self deny: (tree includes: 50). +] + +{ #category : 'tests' } +CTBinarySearchTreeTest >> testSuccessors [ + + tree addAll: #(50 30 70 20 40 60 80). + + self assert: (tree successorOf: 50) equals: 60. + self assert: (tree successorOf: 70) equals: 80. + self assert: (tree successorOf: 80) isNil. +] diff --git a/src/Containers-BinarySearchTree/CTBSTAbstractNode.class.st b/src/Containers-BinarySearchTree/CTBSTAbstractNode.class.st index 8487891..3e8a576 100644 --- a/src/Containers-BinarySearchTree/CTBSTAbstractNode.class.st +++ b/src/Containers-BinarySearchTree/CTBSTAbstractNode.class.st @@ -1,5 +1,9 @@ " -I represent an abstract node for Binary Search Tree. +I am an abstract base class for Binary Search Tree nodes. + +I define the common interface that both regular nodes (CTBSTNode) and nil nodes (CTBSTNillNode) must implement, enabling polymorphic behavior throughout the tree structure. + +I use the Null Object pattern to eliminate special case handling for empty subtrees, making tree algorithms cleaner and more reliable. " Class { #name : 'CTBSTAbstractNode', @@ -29,6 +33,36 @@ CTBSTAbstractNode >> contents: anObject [ self subclassResponsibility ] +{ #category : 'enumerating' } +CTBSTAbstractNode >> elementsFrom: min to: max into: aCollection [ + + ^ self subclassResponsibility +] + +{ #category : 'enumerating' } +CTBSTAbstractNode >> elementsGreaterThan: anObject into: aCollection [ + + ^ self subclassResponsibility +] + +{ #category : 'enumerating' } +CTBSTAbstractNode >> elementsLessThan: anObject into: aCollection [ + + ^ self subclassResponsibility +] + +{ #category : 'searching' } +CTBSTAbstractNode >> findMax [ + + ^ self subclassResponsibility +] + +{ #category : 'searching' } +CTBSTAbstractNode >> findMin [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTBSTAbstractNode >> height [ @@ -65,6 +99,30 @@ CTBSTAbstractNode >> parent: aNode [ parent := aNode ] +{ #category : 'enumerating' } +CTBSTAbstractNode >> postOrderDo: aBlock [ + + ^ self subclassResponsibility +] + +{ #category : 'enumerating' } +CTBSTAbstractNode >> preOrderDo: aBlock [ + + ^ self subclassResponsibility +] + +{ #category : 'searching' } +CTBSTAbstractNode >> predecessorOf: anObject [ + + ^ self subclassResponsibility +] + +{ #category : 'removing' } +CTBSTAbstractNode >> removeValue: anObject [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTBSTAbstractNode >> search: anObject [ @@ -76,3 +134,21 @@ CTBSTAbstractNode >> size [ ^ self subclassResponsibility ] + +{ #category : 'searching' } +CTBSTAbstractNode >> successorOf: anObject [ + + ^ self subclassResponsibility +] + +{ #category : 'testing' } +CTBSTAbstractNode >> validateBSTProperty [ + + ^ self subclassResponsibility +] + +{ #category : 'private' } +CTBSTAbstractNode >> validateBSTPropertyWithMin: min max: max [ + + ^ self subclassResponsibility +] diff --git a/src/Containers-BinarySearchTree/CTBSTNillNode.class.st b/src/Containers-BinarySearchTree/CTBSTNillNode.class.st index 808139a..0fcff28 100644 --- a/src/Containers-BinarySearchTree/CTBSTNillNode.class.st +++ b/src/Containers-BinarySearchTree/CTBSTNillNode.class.st @@ -1,5 +1,9 @@ " -I represent a nil node for Binary Search Tree. +I represent an empty node in a Binary Search Tree using the Null Object pattern. + +I provide default 'do nothing' behavior for all tree operations, eliminating the need for nil checks throughout the tree algorithms. This makes the code cleaner and prevents null pointer errors. + +When elements are added to me, I create and return a new CTBSTNode containing the element, effectively growing the tree. " Class { #name : 'CTBSTNillNode', @@ -29,6 +33,36 @@ CTBSTNillNode >> contents: anObject [ "Do nothing for nil node" ] +{ #category : 'enumerating' } +CTBSTNillNode >> elementsFrom: min to: max into: aCollection [ + + "Do nothing for nill node" +] + +{ #category : 'enumerating' } +CTBSTNillNode >> elementsGreaterThan: anObject into: aCollection [ + + "Do nothing for nill node" +] + +{ #category : 'enumerating' } +CTBSTNillNode >> elementsLessThan: anObject into: aCollection [ + + "Do nothing for nill node" +] + +{ #category : 'searching' } +CTBSTNillNode >> findMax [ + + ^ nil +] + +{ #category : 'searching' } +CTBSTNillNode >> findMin [ + + ^ nil +] + { #category : 'accessing' } CTBSTNillNode >> height [ @@ -53,6 +87,31 @@ CTBSTNillNode >> isLeaf [ ^ false ] +{ #category : 'enumerating' } +CTBSTNillNode >> postOrderDo: aBlock [ + + "Do nothing for nill node" +] + +{ #category : 'enumerating' } +CTBSTNillNode >> preOrderDo: aBlock [ + + "Do nothing for nill node" +] + +{ #category : 'searching' } +CTBSTNillNode >> predecessorOf: anObject [ + + ^ nil +] + +{ #category : 'removing' } +CTBSTNillNode >> removeValue: anObject [ + + "Element not found - return self unchanged" + ^ self +] + { #category : 'accessing' } CTBSTNillNode >> search: anObject [ @@ -64,3 +123,21 @@ CTBSTNillNode >> size [ ^ 0 ] + +{ #category : 'searching' } +CTBSTNillNode >> successorOf: anObject [ + + ^ nil +] + +{ #category : 'testing' } +CTBSTNillNode >> validateBSTProperty [ + + ^ true +] + +{ #category : 'private' } +CTBSTNillNode >> validateBSTPropertyWithMin: min max: max [ + + ^ true +] diff --git a/src/Containers-BinarySearchTree/CTBSTNode.class.st b/src/Containers-BinarySearchTree/CTBSTNode.class.st index 00a26c7..03f4964 100644 --- a/src/Containers-BinarySearchTree/CTBSTNode.class.st +++ b/src/Containers-BinarySearchTree/CTBSTNode.class.st @@ -1,5 +1,10 @@ " -I represent a node for Binary Search Tree. +I represent a node in a Binary Search Tree containing actual data. + +I hold a value and references to left and right child nodes, maintaining the BST invariant where left child < my contents < right child. + +I implement all tree operations recursively, delegating to my children when appropriate. My children are never nil - they are either other CTBSTNode instances or CTBSTNillNode instances. + " Class { #name : 'CTBSTNode', @@ -18,8 +23,9 @@ CTBSTNode >> addChild: anObject [ anObject < contents ifTrue: [ left := left addChild: anObject ] - ifFalse: [ anObject > contents - ifTrue: [ right := right addChild: anObject ] ]. + ifFalse: [ + anObject > contents + ifTrue: [ right := right addChild: anObject ] ]. ^ self ] @@ -35,6 +41,60 @@ CTBSTNode >> contents: anObject [ contents := anObject ] +{ #category : 'enumerating' } +CTBSTNode >> elementsFrom: min to: max into: aCollection [ + "If current node is greater than max, only check left subtree" + contents > max ifTrue: [ + left elementsFrom: min to: max into: aCollection. + ^ self ]. + + "If current node is less than min, only check right subtree" + contents < min ifTrue: [ + right elementsFrom: min to: max into: aCollection. + ^ self ]. + + "Current node is in range, check both subtrees and include current" + left elementsFrom: min to: max into: aCollection. + aCollection add: contents. + right elementsFrom: min to: max into: aCollection +] + +{ #category : 'enumerating' } +CTBSTNode >> elementsGreaterThan: anObject into: aCollection [ + + "If current node is greater than target, check left subtree and include current" + contents > anObject ifTrue: [ + left elementsGreaterThan: anObject into: aCollection. + aCollection add: contents ]. + "Always check right subtree" + right elementsGreaterThan: anObject into: aCollection +] + +{ #category : 'enumerating' } +CTBSTNode >> elementsLessThan: anObject into: aCollection [ + + left elementsLessThan: anObject into: aCollection. + contents < anObject ifFalse: [ ^ self ]. + aCollection add: contents. + right elementsLessThan: anObject into: aCollection +] + +{ #category : 'searching' } +CTBSTNode >> findMax [ + + ^ right isEmpty + ifTrue: [ contents ] + ifFalse: [ right findMax ] +] + +{ #category : 'searching' } +CTBSTNode >> findMin [ + + ^ left isEmpty + ifTrue: [ contents ] + ifFalse: [ left findMin ] +] + { #category : 'accessing' } CTBSTNode >> height [ @@ -82,6 +142,73 @@ CTBSTNode >> left: aNode [ aNode parent: self ] +{ #category : 'enumerating' } +CTBSTNode >> postOrderDo: aBlock [ + + left postOrderDo: aBlock. + right postOrderDo: aBlock. + aBlock value: contents +] + +{ #category : 'enumerating' } +CTBSTNode >> preOrderDo: aBlock [ + + aBlock value: contents. + left preOrderDo: aBlock. + right preOrderDo: aBlock +] + +{ #category : 'searching' } +CTBSTNode >> predecessorOf: anObject [ + + | rightResult | + "Find the largest element smaller than anObject" + + anObject <= contents ifTrue: [ + ^ left predecessorOf: anObject ]. + + "Current node is potential predecessor, check right subtree for better one" + rightResult := right predecessorOf: anObject. + ^ rightResult ifNil: [ contents ] ifNotNil: [ rightResult ] +] + +{ #category : 'removing' } +CTBSTNode >> removeThisNode [ + + "Remove this node and return replacement using standard BST algorithm" + | successorValue | + "Case 1: Leaf node (no children)" + (left isEmpty and: [ right isEmpty ]) + ifTrue: [ ^ CTBSTNillNode new ]. + + "Case 2: Only one child" + left isEmpty ifTrue: [ ^ right ]. + right isEmpty ifTrue: [ ^ left ]. + + "Case 3: Two children - replace with inorder successor" + successorValue := right findMin. + contents := successorValue. + right := right removeValue: successorValue. + ^ self +] + +{ #category : 'removing' } +CTBSTNode >> removeValue: anObject [ + + "Remove node with anObject value and return the new subtree root" + + anObject < contents ifTrue: [ + left := left removeValue: anObject. + ^ self ]. + + anObject > contents ifTrue: [ + right := right removeValue: anObject. + ^ self ]. + + "Found the node to remove" + ^ self removeThisNode +] + { #category : 'accessing' } CTBSTNode >> right [ @@ -109,3 +236,34 @@ CTBSTNode >> size [ ^ 1 + left size + right size ] + +{ #category : 'searching' } +CTBSTNode >> successorOf: anObject [ + + | leftResult | + "Find the smallest element larger than anObject" + + anObject >= contents ifTrue: [ + ^ right successorOf: anObject ]. + + "Current node is potential successor, check left subtree for better one" + leftResult := left successorOf: anObject. + ^ leftResult ifNil: [ contents ] ifNotNil: [ leftResult ] +] + +{ #category : 'testing' } +CTBSTNode >> validateBSTProperty [ + + "Check if BST property holds: left < node < right" + ^ self validateBSTPropertyWithMin: nil max: nil +] + +{ #category : 'private' } +CTBSTNode >> validateBSTPropertyWithMin: min max: max [ + + (min notNil and: [ contents < min ]) ifTrue: [ ^ false ]. + (max notNil and: [ contents > max ]) ifTrue: [ ^ false ]. + + ^ (left isEmpty or: [ left validateBSTPropertyWithMin: min max: contents ]) + and: [ right isEmpty or: [ right validateBSTPropertyWithMin: contents max: max ] ] +] diff --git a/src/Containers-BinarySearchTree/CTBinarySearchTree.class.st b/src/Containers-BinarySearchTree/CTBinarySearchTree.class.st index 19df61e..e2a2eff 100644 --- a/src/Containers-BinarySearchTree/CTBinarySearchTree.class.st +++ b/src/Containers-BinarySearchTree/CTBinarySearchTree.class.st @@ -1,5 +1,17 @@ " -I represent a Binary Search Tree data structure. +I represent a Binary Search Tree data structure that maintains elements in sorted order. + +I provide O(log n) average-case performance for insertion, deletion, and search operations. +Elements are stored according to the BST property: left subtree < node < right subtree. + +Usage: + tree := CTBinarySearchTree new. + tree addAll: #(50 30 70 20 40). + tree asArray. => #(20 30 40 50 70) + tree findMin. => 20 + tree includes: 30. => true + +I support standard collection protocol (do:, collect:, select:) and provide specialized tree operations like range queries and predecessor/successor finding. " Class { #name : 'CTBinarySearchTree', @@ -18,10 +30,120 @@ CTBinarySearchTree >> add: anObject [ ^ anObject ] +{ #category : 'adding' } +CTBinarySearchTree >> addAll: aCollection [ + + aCollection do: [ :each | self add: each ]. + ^ aCollection +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> anySatisfy: aBlock [ + + self inOrderDo: [ :each | + (aBlock value: each) ifTrue: [ ^ true ] + ]. + ^ false +] + +{ #category : 'converting' } +CTBinarySearchTree >> asArray [ + + | result | + result := OrderedCollection new. + self inOrderDo: [ :each | result add: each ]. + ^ result asArray +] + +{ #category : 'removing' } +CTBinarySearchTree >> clear [ + + root := CTBSTNillNode new +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> collect: aBlock [ + + | result | + result := OrderedCollection new. + self inOrderDo: [ :each | result add: (aBlock value: each) ]. + ^ result + +] + +{ #category : 'copying' } +CTBinarySearchTree >> copy [ + + | newTree | + newTree := self class new. + self inOrderDo: [ :each | newTree add: each ]. + ^ newTree +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> detect: aBlock ifNone: absentBlock [ + + self inOrderDo: [ :each | + (aBlock value: each) ifTrue: [ ^ each ] + ]. + ^ absentBlock value +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> do: aBlock [ + + "Alias for inOrderDo: - visits elements in sorted order" + self inOrderDo: aBlock +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> elementsFrom: min to: max [ + + | result | + min > max ifTrue: [ ^ #() ]. + result := OrderedCollection new. + root elementsFrom: min to: max into: result. + ^ result +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> elementsGreaterThan: anObject [ + + | result | + result := OrderedCollection new. + root elementsGreaterThan: anObject into: result. + ^ result +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> elementsLessThan: anObject [ + + | result | + result := OrderedCollection new. + root elementsLessThan: anObject into: result. + ^ result +] + +{ #category : 'searching' } +CTBinarySearchTree >> findMax [ + + ^ self isEmpty + ifTrue: [ nil ] + ifFalse: [ root findMax ] +] + +{ #category : 'searching' } +CTBinarySearchTree >> findMin [ + + ^ self isEmpty + ifTrue: [ nil ] + ifFalse: [ root findMin ] +] + { #category : 'accessing' } CTBinarySearchTree >> height [ - ^ root height + ^ root isEmpty ifTrue: [ 0 ] ifFalse: [ root height ] ] { #category : 'enumerating' } @@ -49,14 +171,85 @@ CTBinarySearchTree >> isEmpty [ ^ root isEmpty ] +{ #category : 'enumerating' } +CTBinarySearchTree >> postOrderDo: aBlock [ + + root postOrderDo: aBlock + + +] + +{ #category : 'enumerating' } +CTBinarySearchTree >> preOrderDo: aBlock [ + + root preOrderDo: aBlock. +] + +{ #category : 'searching' } +CTBinarySearchTree >> predecessorOf: anObject [ + + "Find the predecessor (largest element smaller than anObject)" + ^ root predecessorOf: anObject +] + +{ #category : 'removing' } +CTBinarySearchTree >> remove: anObject [ + + ^ self + remove: anObject + ifAbsent: [ + self error: 'Element not found: ' , anObject printString ] +] + +{ #category : 'removing' } +CTBinarySearchTree >> remove: anObject ifAbsent: aBlock [ + + | originalSize | + originalSize := self size. + root := root removeValue: anObject. + ^ originalSize = self size + ifTrue: [ aBlock value ] + ifFalse: [ anObject ] +] + +{ #category : 'removing' } +CTBinarySearchTree >> removeAll: aCollection [ + + aCollection do: [ :each | self remove: each ifAbsent: [ ] ]. + ^ aCollection +] + { #category : 'accessing' } CTBinarySearchTree >> root [ ^ root isEmpty ifTrue: [ nil ] ifFalse: [ root ] ] +{ #category : 'enumerating' } +CTBinarySearchTree >> select: aBlock [ + + | result | + result := OrderedCollection new. + self inOrderDo: [ :each | (aBlock value: each) ifTrue: [ result add: each ] ]. + ^ result +] + { #category : 'accessing' } CTBinarySearchTree >> size [ ^ root size ] + +{ #category : 'searching' } +CTBinarySearchTree >> successorOf: anObject [ + + "Find the successor (smallest element larger than anObject)" + ^ root successorOf: anObject +] + +{ #category : 'testing' } +CTBinarySearchTree >> validateBSTProperty [ + + "Validate that the tree maintains BST property" + ^ root validateBSTProperty +]