Skip to content

Commit ea699e2

Browse files
committed
Resolve Issues #21, #23, and #24 - Enhance Null Object Pattern and Simplify API
1 parent 0fdf729 commit ea699e2

File tree

4 files changed

+112
-110
lines changed

4 files changed

+112
-110
lines changed

src/Containers-AVL-Tree/CTAVLAbstractNode.class.st

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,17 @@ CTAVLAbstractNode >> checkRemovingPath: path [
3131

3232
{ #category : 'accessing' }
3333
CTAVLAbstractNode >> children [
34-
^ { }
34+
^ #()
35+
]
36+
37+
{ #category : 'enumerating' }
38+
CTAVLAbstractNode >> childrenDo: aBlock [
39+
^ self subclassResponsibility
3540
]
3641

3742
{ #category : 'enumerating' }
3843
CTAVLAbstractNode >> do: aBlock [
44+
"Default is to do nothing"
3945
]
4046

4147
{ #category : 'accessing' }
@@ -48,21 +54,22 @@ CTAVLAbstractNode >> isBalanced [
4854
^ true
4955
]
5056

51-
{ #category : 'testing' }
52-
CTAVLAbstractNode >> isNilNode [
53-
^ false
54-
]
55-
5657
{ #category : 'testing' }
5758
CTAVLAbstractNode >> isTotalBalanced [
5859
^ true
5960
]
6061

62+
{ #category : 'accessing' }
63+
CTAVLAbstractNode >> nodeSize [
64+
^ self subclassResponsibility
65+
]
66+
6167
{ #category : 'removing' }
6268
CTAVLAbstractNode >> remove: anObject path: list [
6369
^ nil
6470
]
6571

6672
{ #category : 'accessing' }
6773
CTAVLAbstractNode >> withAllChildren: aCollection [
68-
]
74+
"Default is to do nothing"
75+
]

src/Containers-AVL-Tree/CTAVLNilNode.class.st

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
"
22
AVLNilNode is a special sentinel node used in AVL trees to represent the absence of a node.
33
4-
In an AVL tree, `AVLNilNode` is used to represent the missing node. It serves as a placeholder for null references, making it easier to perform tree operations without having to deal with special cases for missing children.
5-
6-
`AVLNilNode` is a subclass of `AVLAbstractNode`, and it provides default implementations for methods that are specific to nil nodes, such as `addChild:` and `isNilNode`.
7-
8-
This class allows AVL trees to be implemented more cleanly and efficiently by treating missing nodes as instances of `AVLNilNode`.
4+
In an AVL tree, `AVLNilNode` is used as a placeholder for null references, making it easier to perform tree operations without special cases for missing children. It fully implements the Null Object pattern.
95
106
Author: Milton Mamani
117
Date: October 20, 2023
@@ -24,9 +20,50 @@ CTAVLNilNode >> addChild: newObject [
2420

2521
{ #category : 'private' }
2622
CTAVLNilNode >> checkRemovingPath: path [
23+
"Do nothing for nil node"
24+
]
25+
26+
{ #category : 'accessing' }
27+
CTAVLNilNode >> children [
28+
^ #()
29+
]
30+
31+
{ #category : 'enumerating' }
32+
CTAVLNilNode >> childrenDo: aBlock [
33+
"Do nothing for nil node"
34+
]
35+
36+
{ #category : 'enumerating' }
37+
CTAVLNilNode >> do: aBlock [
38+
"Do nothing for nil node"
39+
]
40+
41+
{ #category : 'accessing' }
42+
CTAVLNilNode >> height [
43+
^ 0
2744
]
2845

2946
{ #category : 'testing' }
30-
CTAVLNilNode >> isNilNode [
47+
CTAVLNilNode >> isBalanced [
3148
^ true
3249
]
50+
51+
{ #category : 'testing' }
52+
CTAVLNilNode >> isTotalBalanced [
53+
^ true
54+
]
55+
56+
{ #category : 'accessing' }
57+
CTAVLNilNode >> nodeSize [
58+
^ 0
59+
]
60+
61+
{ #category : 'removing' }
62+
CTAVLNilNode >> remove: anObject path: list [
63+
^ nil
64+
]
65+
66+
{ #category : 'accessing' }
67+
CTAVLNilNode >> withAllChildren: aCollection [
68+
"Do nothing for nil node"
69+
]

src/Containers-AVL-Tree/CTAVLNode.class.st

Lines changed: 48 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ AVLNode represents a node in an AVL tree.
33
44
An AVL tree is a self-balancing binary search tree where the heights of the two child subtrees of every node differ by at most one. The `AVLNode` class extends the `AVLAbstractNode` class and provides the implementation of actual nodes within the AVL tree.
55
6-
AVLNode instances hold a `contents` instance variable. These nodes are organized in such a way that the tree remains balanced, ensuring efficient operations like insertion, deletion, and search.
6+
AVLNode instances hold a `contents` instance variable. These nodes are organized to keep the tree balanced, ensuring efficient operations like insertion, deletion, and search.
77
88
This class should not be used directly; instead use `AVLTree`.
99
@@ -31,28 +31,20 @@ Class {
3131
CTAVLNode class >> with: anInteger [
3232
^ self new
3333
contents: anInteger;
34+
left: CTAVLNilNode new;
35+
right: CTAVLNilNode new;
3436
yourself
3537
]
3638

3739
{ #category : 'adding' }
3840
CTAVLNode >> add: anInteger path: list [
39-
anInteger < contents ifTrue: [
40-
left
41-
ifNil: [
42-
left := self class with: anInteger.
43-
list add: false -> left ]
44-
ifNotNil: [
45-
list add: false -> left.
46-
left add: anInteger path: list ]
41+
anInteger < contents ifTrue: [
42+
list add: false -> left.
43+
left := left addChild: anInteger.
4744
] ifFalse: [
48-
right
49-
ifNil: [
50-
right := self class with: anInteger.
51-
list add: true -> right ]
52-
ifNotNil: [
53-
list add: true -> right.
54-
right add: anInteger path: list ] ]
55-
45+
list add: true -> right.
46+
right := right addChild: anInteger.
47+
]
5648
]
5749

5850
{ #category : 'adding' }
@@ -81,7 +73,6 @@ CTAVLNode >> balance: index path: aCollection [
8173
^ self lrrotationZ: c y: b x: a ].
8274
"(y key and: [ x key not ])"
8375
^ self rlrotationZ: c y: b x: a.
84-
"self notYetImplemented."
8576
]
8677

8778
{ #category : 'private' }
@@ -98,7 +89,6 @@ CTAVLNode >> balanceZ: z y: y x: x [
9889
^ self lrrotationZ: c y: b x: a ].
9990
"(y key and: [ x key not ])"
10091
^ self rlrotationZ: c y: b x: a.
101-
"self notYetImplemented."
10292
]
10393

10494
{ #category : 'private' }
@@ -126,13 +116,13 @@ CTAVLNode >> checkRemovingPath: path [
126116

127117
{ #category : 'accessing' }
128118
CTAVLNode >> children [
129-
^ { left. right } reject: #isNil
119+
^ { left. right }
130120
]
131121

132122
{ #category : 'enumerating' }
133-
CTAVLNode >> childrenDo: aFullBlockClosure [
134-
left ifNotNil: aFullBlockClosure.
135-
right ifNotNil: aFullBlockClosure.
123+
CTAVLNode >> childrenDo: aBlock [
124+
left childrenDo: aBlock.
125+
right childrenDo: aBlock.
136126
]
137127

138128
{ #category : 'accessing' }
@@ -146,49 +136,39 @@ CTAVLNode >> contents: anInteger [
146136
]
147137

148138
{ #category : 'enumerating' }
149-
CTAVLNode >> do: aFullBlockClosure [
150-
left ifNotNil: [ left do: aFullBlockClosure ].
151-
aFullBlockClosure value: self contents.
152-
right ifNotNil: [ right do: aFullBlockClosure ].
139+
CTAVLNode >> do: aBlock [
140+
left do: aBlock.
141+
aBlock value: self contents.
142+
right do: aBlock.
143+
]
144+
145+
{ #category : 'testing' }
146+
CTAVLNode >> hasNoChildren [
147+
^ left nodeSize = 0 and: [ right nodeSize = 0 ]
153148
]
154149

155150
{ #category : 'accessing' }
156151
CTAVLNode >> height [
157-
| leftHeight rightHeight |
158-
leftHeight := left ifNil: [ 0 ] ifNotNil: [ left height ].
159-
rightHeight := right ifNil: [ 0 ] ifNotNil: [ right height ].
160-
^ (leftHeight max: rightHeight) + 1
161-
152+
^ (left height max: right height) + 1
162153
]
163154

164155
{ #category : 'testing' }
165156
CTAVLNode >> isBalanced [
166-
| leftHeight rightHeight |
167-
leftHeight := left ifNil: [ 0 ] ifNotNil: [ left height ].
168-
rightHeight := right ifNil: [ 0 ] ifNotNil: [ right height ].
169-
170-
^ (leftHeight - rightHeight) abs <= 1
171-
]
172-
173-
{ #category : 'testing' }
174-
CTAVLNode >> isLeaf [
175-
^ left isNil and: [ right isNil ]
157+
^ (left height - right height) abs <= 1
176158
]
177159

178160
{ #category : 'testing' }
179161
CTAVLNode >> isTotalBalanced [
180162
^ self isBalanced
181-
and: [ (left isNil or: [ left isTotalBalanced ])
182-
and: [ right isNil or: [ right isTotalBalanced ] ] ]
183-
184-
163+
and: [ left isTotalBalanced
164+
and: [ right isTotalBalanced ] ]
185165
]
186166

187167
{ #category : 'accessing' }
188168
CTAVLNode >> largerNode [
189169
| size1 size2 isLeft |
190-
size1 := left ifNil: [ 0 ] ifNotNil: [ left height ].
191-
size2 := right ifNil: [ 0 ] ifNotNil: [ right height ].
170+
size1 := left height.
171+
size2 := right height.
192172
isLeft := size1 > size2.
193173
^ isLeft not -> (isLeft ifTrue: [ left ] ifFalse: [ right ])
194174
]
@@ -212,7 +192,6 @@ CTAVLNode >> llrotationZ: z y: y x: x [
212192
new := self class with: z contents.
213193
new left: a3; right: a4.
214194
z left: x; contents: y contents; right: new.
215-
216195
]
217196

218197
{ #category : 'private' }
@@ -228,6 +207,11 @@ CTAVLNode >> lrrotationZ: z y: y x: x [
228207
self llrotationZ: z y: y x: new
229208
]
230209

210+
{ #category : 'accessing' }
211+
CTAVLNode >> nodeSize [
212+
^ 1 + left nodeSize + right nodeSize
213+
]
214+
231215
{ #category : 'printing' }
232216
CTAVLNode >> printOn: stream [
233217
contents printOn: stream
@@ -239,10 +223,8 @@ CTAVLNode >> remove: anObject path: list [
239223
^ self
240224
] ifFalse: [
241225
| node nodeToRemove isLeft |
242-
node := (isLeft := anObject < contents)
243-
ifTrue: [ left ]
244-
ifFalse: [ right ].
245-
node ifNil: [ ^ nil ].
226+
isLeft := anObject < contents.
227+
node := isLeft ifTrue: [ left ] ifFalse: [ right ].
246228
list add: self.
247229
nodeToRemove := node remove: anObject path: list.
248230
nodeToRemove == node ifTrue: [
@@ -251,24 +233,24 @@ CTAVLNode >> remove: anObject path: list [
251233
isLeft
252234
ifTrue: [ left := successor ]
253235
ifFalse: [ right := successor ]
254-
] .
236+
].
255237
^ nodeToRemove
256238
].
257239
]
258240

259241
{ #category : 'removing' }
260242
CTAVLNode >> removeMinimum: list [
261243
| res |
262-
left ifNil: [
244+
left nodeSize = 0 ifTrue: [
263245
res := self class with: contents.
264246
contents := right contents.
265247
left := right left.
266248
right := right right ]
267-
ifNotNil: [
249+
ifFalse: [
268250
list add: self.
269-
left isLeaf ifTrue: [
251+
left hasNoChildren ifTrue: [
270252
res := left.
271-
left := nil ]
253+
left := CTAVLNilNode new ]
272254
ifFalse: [ res := left removeMinimum: list ] ].
273255
^ res
274256
]
@@ -298,37 +280,28 @@ CTAVLNode >> rlrotationZ: z y: y x: x [
298280

299281
{ #category : 'private' }
300282
CTAVLNode >> rrrotationZ: z y: y x: x [
301-
"right right rotation"
302283
| a1 a2 new |
303284
a1 := z left.
304285
a2 := y left.
305286

306287
new := self class with: z contents.
307288
new left: a1; right: a2.
308289
z left: new; right: x; contents: y contents
309-
310290
]
311291

312292
{ #category : 'search' }
313293
CTAVLNode >> search: anInteger [
314-
^ contents = anInteger ifTrue: [
315-
contents
316-
] ifFalse: [
317-
| node |
318-
node := anInteger < contents
319-
ifTrue: [ left ]
320-
ifFalse: [ right ].
321-
node ifNil: [ nil ] ifNotNil: [ node search: anInteger ]
322-
]
294+
contents = anInteger ifTrue: [ ^ contents ].
295+
^ (anInteger < contents ifTrue: [ left ] ifFalse: [ right ]) search: anInteger
323296
]
324297

325298
{ #category : 'removing' }
326299
CTAVLNode >> successor: list [
327-
^ self isLeaf
328-
ifTrue: [ nil ]
300+
^ self hasNoChildren
301+
ifTrue: [ CTAVLNilNode new ]
329302
ifFalse: [
330-
(left notNil and: [ right notNil ]) ifTrue: [
331-
right isLeaf
303+
(left nodeSize > 0 and: [ right nodeSize > 0 ]) ifTrue: [
304+
right hasNoChildren
332305
ifTrue: [ list add: (right left: left; yourself) ]
333306
ifFalse: [ | min newList |
334307
newList := OrderedCollection new.
@@ -339,12 +312,11 @@ CTAVLNode >> successor: list [
339312
min right: right.
340313
right := min ]
341314
] ifFalse: [
342-
list add: (left ifNil: [ right ] ifNotNil: [ left ]) ] ].
343-
315+
list add: (left nodeSize = 0 ifTrue: [ right ] ifFalse: [ left ]) ] ]
344316
]
345317

346318
{ #category : 'accessing' }
347319
CTAVLNode >> withAllChildren: aCollection [
348320
aCollection add: self.
349321
self childrenDo: [ :child | child withAllChildren: aCollection ]
350-
]
322+
]

0 commit comments

Comments
 (0)