Skip to content

Commit dfe7957

Browse files
committed
Added new methods & improved error handling
1 parent dcf17c6 commit dfe7957

File tree

5 files changed

+121
-47
lines changed

5 files changed

+121
-47
lines changed

src/Containers-BinarySearchTree-Tests/CTBinarySearchTreeTest.class.st

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ CTBinarySearchTreeTest >> testAsArray [
4848
self assert: result equals: #(20 30 40 50 70)
4949
]
5050

51+
{ #category : 'tests' }
52+
CTBinarySearchTreeTest >> testAtIfAbsent [
53+
54+
tree addAll: #(50 30 70).
55+
self assert: (tree at: 30 ifAbsent: [ #notFound ]) equals: 30.
56+
self assert: (tree at: 99 ifAbsent: [ #notFound ]) equals: #notFound
57+
]
58+
5159
{ #category : 'tests' }
5260
CTBinarySearchTreeTest >> testBSTValidation [
5361

@@ -186,12 +194,13 @@ CTBinarySearchTreeTest >> testEmptyTreeOperations [
186194
]
187195

188196
{ #category : 'tests' }
189-
CTBinarySearchTreeTest >> testFindMinMax [
197+
CTBinarySearchTreeTest >> testFirstLast [
190198

191199
tree addAll: #(50 30 70 20 80).
192-
193-
self assert: tree findMin equals: 20.
194-
self assert: tree findMax equals: 80
200+
self assert: tree first equals: 20.
201+
self assert: tree last equals: 80.
202+
self assert: CTBinarySearchTree new first isNil.
203+
self assert: CTBinarySearchTree new last isNil
195204
]
196205

197206
{ #category : 'tests' }
@@ -362,16 +371,20 @@ CTBinarySearchTreeTest >> testRemoveNonExistentElement [
362371
{ #category : 'tests' }
363372
CTBinarySearchTreeTest >> testRemoveRoot [
364373
"Root with no children"
374+
365375
tree add: 50.
366376
tree remove: 50.
367377
self assert: tree isEmpty.
368-
378+
369379
"Root with two children"
370380
tree clear.
371-
tree addAll: #(50 30 70 20 40 60 80).
381+
tree addAll: #( 50 30 70 20 40 60 80 ).
372382
tree remove: 50.
373383
self assert: tree size equals: 6.
374384
self deny: (tree includes: 50).
385+
386+
self assert: (tree includes: 60).
387+
self assert: tree root contents equals: 60
375388
]
376389

377390
{ #category : 'tests' }

src/Containers-BinarySearchTree/CTBSTAbstractNode.class.st

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,24 @@ CTBSTAbstractNode >> findMax [
5757
^ self subclassResponsibility
5858
]
5959

60+
{ #category : 'searching' }
61+
CTBSTAbstractNode >> findMaxNode [
62+
63+
^ self subclassResponsibility
64+
]
65+
6066
{ #category : 'searching' }
6167
CTBSTAbstractNode >> findMin [
6268

6369
^ self subclassResponsibility
6470
]
6571

72+
{ #category : 'searching' }
73+
CTBSTAbstractNode >> findMinNode [
74+
75+
^ self subclassResponsibility
76+
]
77+
6678
{ #category : 'accessing' }
6779
CTBSTAbstractNode >> height [
6880

src/Containers-BinarySearchTree/CTBSTNilNode.class.st

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,24 @@ CTBSTNilNode >> findMax [
5757
^ nil
5858
]
5959

60+
{ #category : 'searching' }
61+
CTBSTNilNode >> findMaxNode [
62+
63+
^ self
64+
]
65+
6066
{ #category : 'searching' }
6167
CTBSTNilNode >> findMin [
6268

6369
^ nil
6470
]
6571

72+
{ #category : 'searching' }
73+
CTBSTNilNode >> findMinNode [
74+
75+
^ self
76+
]
77+
6678
{ #category : 'accessing' }
6779
CTBSTNilNode >> height [
6880

src/Containers-BinarySearchTree/CTBSTNode.class.st

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,29 @@ CTBSTNode >> elementsLessThan: anObject into: aCollection [
7373

7474
{ #category : 'searching' }
7575
CTBSTNode >> findMax [
76+
^ self findMaxNode contents
77+
]
78+
79+
{ #category : 'searching' }
80+
CTBSTNode >> findMaxNode [
7681

7782
^ right isEmpty
78-
ifTrue: [ contents ]
79-
ifFalse: [ right findMax ]
83+
ifTrue: [ self ]
84+
ifFalse: [ right findMaxNode ]
8085
]
8186

8287
{ #category : 'searching' }
8388
CTBSTNode >> findMin [
8489

90+
^ self findMinNode contents
91+
]
92+
93+
{ #category : 'searching' }
94+
CTBSTNode >> findMinNode [
95+
8596
^ left isEmpty
86-
ifTrue: [ contents ]
87-
ifFalse: [ left findMin ]
97+
ifTrue: [ self ]
98+
ifFalse: [ left findMinNode ]
8899
]
89100

90101
{ #category : 'accessing' }
@@ -166,38 +177,33 @@ CTBSTNode >> predecessorOf: anObject [
166177

167178
{ #category : 'removing' }
168179
CTBSTNode >> removeThisNode [
169-
170-
"Remove this node and return replacement using standard BST algorithm"
171-
| successorValue |
172-
"Case 1: Leaf node (no children)"
173-
(left isEmpty and: [ right isEmpty ])
174-
ifTrue: [ ^ CTBSTNilNode new ].
175-
176-
"Case 2: Only one child"
180+
"Remove this specific node and return its replacement.
181+
The caller is responsible for linking the replacement into the tree."
182+
| successor |
183+
"Case 1 & 2: Leaf or single child. Return the non-empty child or the nil node."
177184
left isEmpty ifTrue: [ ^ right ].
178185
right isEmpty ifTrue: [ ^ left ].
179-
180-
"Case 3: Two children - replace with inorder successor"
181-
successorValue := right findMin.
182-
contents := successorValue.
183-
right := right removeValue: successorValue.
186+
187+
"Case 3: Two children. Replace contents with inorder successor's contents,
188+
then remove the successor node from the right subtree."
189+
successor := right findMinNode.
190+
self contents: successor contents.
191+
self right: (right removeValue: successor contents).
184192
^ self
185193
]
186194

187195
{ #category : 'removing' }
188196
CTBSTNode >> removeValue: anObject [
189-
190-
"Remove node with anObject value and return the new subtree root"
191-
197+
"Find the node with anObject and remove it, returning the new root of this subtree."
192198
anObject < contents ifTrue: [
193-
left := left removeValue: anObject.
199+
self left: (left removeValue: anObject).
194200
^ self ].
195-
201+
196202
anObject > contents ifTrue: [
197-
right := right removeValue: anObject.
203+
self right: (right removeValue: anObject).
198204
^ self ].
199-
200-
"Found the node to remove"
205+
206+
"Found the node to remove. `removeThisNode` will return the replacement."
201207
^ self removeThisNode
202208
]
203209

src/Containers-BinarySearchTree/CTBinarySearchTree.class.st

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ CTBinarySearchTree >> asArray [
5656
^ result asArray
5757
]
5858

59+
{ #category : 'accessing' }
60+
CTBinarySearchTree >> at: anObject ifAbsent: aBlock [
61+
62+
| result |
63+
result := root search: anObject.
64+
^ result ifNil: [ aBlock value ] ifNotNil: [ result ]
65+
]
66+
5967
{ #category : 'removing' }
6068
CTBinarySearchTree >> clear [
6169

@@ -124,20 +132,22 @@ CTBinarySearchTree >> elementsLessThan: anObject [
124132
^ result
125133
]
126134

127-
{ #category : 'searching' }
128-
CTBinarySearchTree >> findMax [
135+
{ #category : 'accessing' }
136+
CTBinarySearchTree >> findMax [
129137

130-
^ self isEmpty
131-
ifTrue: [ nil ]
132-
ifFalse: [ root findMax ]
138+
^ root findMax
133139
]
134140

135-
{ #category : 'searching' }
141+
{ #category : 'accessing' }
136142
CTBinarySearchTree >> findMin [
137143

138-
^ self isEmpty
139-
ifTrue: [ nil ]
140-
ifFalse: [ root findMin ]
144+
^ root findMin
145+
]
146+
147+
{ #category : 'accessing' }
148+
CTBinarySearchTree >> first [
149+
150+
^ self findMin
141151
]
142152

143153
{ #category : 'accessing' }
@@ -146,6 +156,22 @@ CTBinarySearchTree >> height [
146156
^ root isEmpty ifTrue: [ 0 ] ifFalse: [ root height ]
147157
]
148158

159+
{ #category : 'testing' }
160+
CTBinarySearchTree >> ifEmpty: aBlock [
161+
162+
^ self isEmpty
163+
ifTrue: [ aBlock value ]
164+
ifFalse: [ self ]
165+
]
166+
167+
{ #category : 'testing' }
168+
CTBinarySearchTree >> ifNotEmpty: aBlock [
169+
170+
^ self isEmpty
171+
ifFalse: [ aBlock value: self ]
172+
ifTrue: [ self ]
173+
]
174+
149175
{ #category : 'enumerating' }
150176
CTBinarySearchTree >> inOrderDo: aBlock [
151177

@@ -155,7 +181,7 @@ CTBinarySearchTree >> inOrderDo: aBlock [
155181
{ #category : 'testing' }
156182
CTBinarySearchTree >> includes: anObject [
157183

158-
^ (root search: anObject) notNil
184+
^ (self at: anObject ifAbsent: [ nil ]) notNil
159185
]
160186

161187
{ #category : 'initialization' }
@@ -171,6 +197,12 @@ CTBinarySearchTree >> isEmpty [
171197
^ root isEmpty
172198
]
173199

200+
{ #category : 'accessing' }
201+
CTBinarySearchTree >> last [
202+
203+
^ self findMax
204+
]
205+
174206
{ #category : 'enumerating' }
175207
CTBinarySearchTree >> postOrderDo: aBlock [
176208

@@ -198,18 +230,17 @@ CTBinarySearchTree >> remove: anObject [
198230
^ self
199231
remove: anObject
200232
ifAbsent: [
201-
self error: 'Element not found: ' , anObject printString ]
233+
NotFound signalFor: anObject in: self ]
202234
]
203235

204236
{ #category : 'removing' }
205237
CTBinarySearchTree >> remove: anObject ifAbsent: aBlock [
206238

207-
| originalSize |
208-
originalSize := self size.
239+
(self includes: anObject) ifFalse: [ ^ aBlock value ].
240+
209241
root := root removeValue: anObject.
210-
^ originalSize = self size
211-
ifTrue: [ aBlock value ]
212-
ifFalse: [ anObject ]
242+
root parent: nil. "The new root has no parent"
243+
^ anObject
213244
]
214245

215246
{ #category : 'removing' }

0 commit comments

Comments
 (0)