From 5ac8f31e2de8dc2467bb9a25c17cd343133cbb2c Mon Sep 17 00:00:00 2001 From: Alokzh Date: Thu, 31 Jul 2025 17:08:42 +0530 Subject: [PATCH 01/10] Remove old methods + tests & add new basic AVL Tree implementation structure --- .../CTAVLTreeTest.class.st | 333 +----------------- .../CTAVLAbstractNode.class.st | 62 +--- src/Containers-AVL-Tree/CTAVLNilNode.class.st | 52 +-- src/Containers-AVL-Tree/CTAVLNode.class.st | 286 ++------------- src/Containers-AVL-Tree/CTAVLTree.class.st | 73 +--- 5 files changed, 63 insertions(+), 743 deletions(-) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index 18e27d1..c25a2dd 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -13,340 +13,23 @@ Class { { #category : 'running' } CTAVLTreeTest >> setUp [ - + super setUp. tree := CTAVLTree new ] { #category : 'tests' } -CTAVLTreeTest >> testAddForLLrotation [ - - | data | - tree add: 3. - self assert: tree isBalanced. - tree add: 2. - self assert: tree isBalanced. - tree add: 1. - self assert: tree isBalanced. - data := tree collect: #yourself. - self assert: data asArray equals: { 1. 2. 3 } -] - -{ #category : 'tests' } -CTAVLTreeTest >> testAddForRRrotation [ - - | data | - tree add: 1. - self assert: tree isBalanced. - tree add: 2. - self assert: tree isBalanced. - tree add: 3. - self assert: tree isBalanced. - data := tree collect: #yourself. - self assert: data asArray equals: { 1. 2. 3 } -] - -{ #category : 'tests' } -CTAVLTreeTest >> testAddOneElement [ - - tree add: 1. - self assert: tree isNotEmpty. - self assert: (tree includes: 1) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testAddTreeElements [ - - tree addAll: { 2. 4. 10 }. - self assert: (tree includes: 2). - self assert: (tree includes: 4). - self assert: (tree includes: 10) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testAllChildren [ - - | allChildren | - allChildren := tree allChildren. - self assert: allChildren isEmpty. - tree addAll: { 4. 2. 3. 10 }. - allChildren := tree allChildren. - self assert: allChildren size equals: 4. - self assert: allChildren first class equals: CTAVLNode -] - -{ #category : 'tests' } -CTAVLTreeTest >> testDuplicateElement [ - tree add: 42. - "Adding the same element again should maintain balance" - tree add: 42. - self assert: tree isBalanced. - "Removing one instance of the element should keep the other" - tree remove: 42. - self assert: (tree includes: 42). - tree remove: 42. - self assert: tree isEmpty -] - -{ #category : 'tests' } -CTAVLTreeTest >> testEmpty [ - - self assert: tree isEmpty -] - -{ #category : 'tests' } -CTAVLTreeTest >> testHeight [ - - self assert: tree height equals: 0. - tree add: 4. - self assert: tree height equals: 1. - tree add: 2. - self assert: tree height equals: 2. - tree add: 10. - self assert: tree height equals: 2 -] - -{ #category : 'tests' } -CTAVLTreeTest >> testIsBalanced [ - - self assert: tree isBalanced. - tree add: 4. - self assert: tree isBalanced. - tree add: 2. - self assert: tree isBalanced. - tree add: 10. - self assert: tree isBalanced -] - -{ #category : 'tests' } -CTAVLTreeTest >> testIsLeaf [ - - self assert: tree isNil. - tree add: 1. - self assert: tree isLeaf. - - tree add: 10. - self deny: tree isLeaf. - - tree:= CTAVLTree new. - tree add: 10. - tree add: 5. - self deny: tree isLeaf. -] - -{ #category : 'tests' } -CTAVLTreeTest >> testLRRotation [ - "Test LR rotation" -|data| -tree := CTAVLTree new. -tree add: 1. -tree add: 3. -tree add: 2. -self assert: tree isBalanced. -data := tree collect: #yourself. -self assert: data asArray equals: { 1. 2. 3 }. -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemove [ - - tree addAll: { 9. 4. 16. 7. 13. 19. 15 }. - tree add: 10. - tree add: 14. - tree add: 11. - tree remove: 13. - self assert: tree isTotalBalanced -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveBranchWithOneChild [ - - tree addAll: { 9. 4. 16. 13. 15 }. - tree add: 14. - tree remove: 9. - self deny: (tree includes: 9). - self assert: (tree includes: 14) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveBranchWithTwoChildren [ - - tree addAll: { 9. 4. 16. 13. 15. 10 }. - tree add: 14. - tree remove: 15. - self deny: (tree includes: 15). - self assert: (tree includes: 14). - self assert: (tree includes: 16) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveBranchWithTwoChildren2 [ +CTAVLTreeTest >> testClear [ - tree addAll: { 9. 4. 16. 13. 15. 10. 14. 20 }. - tree remove: 15. - self deny: (tree includes: 15). - self assert: (tree includes: 20). - self assert: (tree includes: 14). - self assert: (tree includes: 16) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveBranchWithTwoChildren3 [ - - tree addAll: { 9. 4. 17. 13. 15. 10. 14. 20. 16 }. - tree remove: 15. - self deny: (tree includes: 15). - self assert: (tree includes: 20). - self assert: (tree includes: 14). - self assert: (tree includes: 16). - self assert: (tree includes: 17). - self assert: tree isBalanced -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveFromEmptyTree [ - | executed | - executed := false. - tree remove: 42 ifAbsent: [ executed := true ]. - self assert: executed description: 'The ifAbsent: block should execute when removing from an empty tree'. - self assert: tree isEmpty description: 'Tree should remain empty after removal attempt'. -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveRootChildren [ - - tree addAll: { 9 }. - tree remove: 9. - self assert: tree isEmpty -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveRootChildren1 [ - - tree addAll: { 9. 7. 10 }. - tree remove: 10. - self assert: tree size equals: 2. - self assert: (tree includes: 9). - self assert: (tree includes: 7) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveRootChildren2 [ - - tree addAll: { 9. 7 }. - tree remove: 7. - self assert: tree size equals: 1. - self assert: (tree includes: 9) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveRootChildren3 [ - - tree addAll: { 9. 7. 10 }. - tree remove: 9. - self assert: tree size equals: 2. - self deny: (tree includes: 9). - self assert: (tree includes: 10). - self assert: (tree includes: 7) -] - -{ #category : 'tests' } -CTAVLTreeTest >> testRemoveZero [ - - | value | - value := false. - tree remove: 1 ifAbsent: [ value := true ]. - self assert: value -] - -{ #category : 'tests' } -CTAVLTreeTest >> testSearch [ - - tree addAll: { 4. 2. 3. 10 }. - self assert: (tree search: 5) isNil. - self assert: (tree search: 4) equals: 4. - self assert: (tree search: 2) equals: 2. - self assert: (tree search: 10) equals: 10 -] - -{ #category : 'tests' } -CTAVLTreeTest >> testSeriousAdd [ - - tree addAll: { 9. 4. 16. 7. 13. 19. 15 }. - self assert: tree isBalanced. - tree add: 10. - self assert: tree isBalanced. - tree add: 14. - self assert: tree isBalanced -] - -{ #category : 'tests' } -CTAVLTreeTest >> testSeriousRandomAdd [ - - | r size numbers | - r := Random new. - r seed: 1234. - size := 50. - numbers := (1 to: size) collect: [ :i | r nextInteger: size ]. - numbers := numbers asSet. - numbers do: [ :n | - tree add: n. - self assert: tree isBalanced ] -] - -{ #category : 'tests' } -CTAVLTreeTest >> testSeriousRandomRemove [ - - | r size numbers toRemove | - r := Random new. - r seed: 1234. - size := 50. - numbers := (1 to: size) collect: [ :i | r nextInteger: size ]. - numbers := numbers asSet. - tree addAll: numbers. - self assert: tree isTotalBalanced. - toRemove := (1 to: 20) collect: [ :i | numbers atRandom: r ] as: Set. - toRemove do: [ :number | - tree remove: number. - self assert: tree isTotalBalanced ] -] - -{ #category : 'tests' } -CTAVLTreeTest >> testSizeAfterRemoval [ - tree addAll: #(1 2 3 4 5). - tree remove: 3. - self assert: tree size equals: 4 -] - -{ #category : 'tests' } -CTAVLTreeTest >> testSizeEmpty [ + tree clear. + self assert: tree isEmpty. self assert: tree size equals: 0 ] { #category : 'tests' } -CTAVLTreeTest >> testSizeMultipleElements [ - tree addAll: #(1 2 3 4 5). - self assert: tree size equals: 5 -] - -{ #category : 'tests' } -CTAVLTreeTest >> testSizeOneElement [ - tree add: 42. - self assert: tree size equals: 1 -] +CTAVLTreeTest >> testEmpty [ -{ #category : 'tests' } -CTAVLTreeTest >> testChildParentRelationship [ - tree add: 50. - tree add: 30. - tree add: 70. - - self assert: (tree root left parent) equals: tree root. - self assert: (tree root right parent) equals: tree root + self assert: tree isEmpty. + self assert: tree size equals: 0. + self assert: tree height equals: 0 ] - -{ #category : 'tests' } -CTAVLTreeTest >> testRootHasNoParent [ - tree add: 14. - self assert: tree root parent isNil -] \ No newline at end of file diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index 6361774..eef439b 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -22,72 +22,32 @@ Class { #package : 'Containers-AVL-Tree' } -{ #category : 'adding' } -CTAVLAbstractNode >> addChild: newObject [ - ^ self subclassResponsibility -] - -{ #category : 'private' } -CTAVLAbstractNode >> checkRemovingPath: path [ - self subclassResponsibility -] - { #category : 'accessing' } -CTAVLAbstractNode >> children [ - ^ #() -] +CTAVLAbstractNode >> height [ -{ #category : 'accessing' } -CTAVLAbstractNode >> allChildren [ - ^ #() + ^ self subclassResponsibility ] -{ #category : 'enumerating' } -CTAVLAbstractNode >> childrenDo: aBlock [ - ^ self subclassResponsibility -] +{ #category : 'testing' } +CTAVLAbstractNode >> isEmpty [ -{ #category : 'enumerating' } -CTAVLAbstractNode >> do: aBlock [ - "Default is to do nothing" + ^ self subclassResponsibility ] { #category : 'accessing' } -CTAVLAbstractNode >> height [ - ^ 0 -] - -{ #category : 'testing' } -CTAVLAbstractNode >> isBalanced [ - ^ true -] +CTAVLAbstractNode >> parent [ -{ #category : 'testing' } -CTAVLAbstractNode >> isTotalBalanced [ - ^ true + ^ parent ] { #category : 'accessing' } -CTAVLAbstractNode >> nodeSize [ - ^ self subclassResponsibility -] +CTAVLAbstractNode >> parent: aNode [ -{ #category : 'removing' } -CTAVLAbstractNode >> remove: anObject path: list [ - ^ nil + parent := aNode ] { #category : 'accessing' } -CTAVLAbstractNode >> withAllChildren: aCollection [ - "Default is to do nothing" -] +CTAVLAbstractNode >> size [ -{ #category : 'accessing' } -CTAVLAbstractNode >> parent [ - ^ parent + ^ self subclassResponsibility ] - -{ #category : 'accessing' } -CTAVLAbstractNode >> parent: aNode [ - parent := aNode -] \ No newline at end of file diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index 1668f45..a4911cf 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -13,62 +13,20 @@ Class { #package : 'Containers-AVL-Tree' } -{ #category : 'adding' } -CTAVLNilNode >> addChild: newObject [ - ^ self parent replace: self with: (CTAVLNode with: newObject) -] - -{ #category : 'private' } -CTAVLNilNode >> checkRemovingPath: path [ - "Do nothing for nil node" -] - -{ #category : 'accessing' } -CTAVLNilNode >> children [ - ^ #() -] - -{ #category : 'accessing' } -CTAVLNilNode >> allChildren [ - ^ #() -] - -{ #category : 'enumerating' } -CTAVLNilNode >> childrenDo: aBlock [ - "Do nothing for nil node" -] - -{ #category : 'enumerating' } -CTAVLNilNode >> do: aBlock [ - "Do nothing for nil node" -] - { #category : 'accessing' } -CTAVLNilNode >> height [ +CTAVLNilNode >> height [ + ^ 0 ] { #category : 'testing' } -CTAVLNilNode >> isBalanced [ - ^ true -] +CTAVLNilNode >> isEmpty [ -{ #category : 'testing' } -CTAVLNilNode >> isTotalBalanced [ ^ true ] { #category : 'accessing' } -CTAVLNilNode >> nodeSize [ - ^ 0 -] +CTAVLNilNode >> size [ -{ #category : 'removing' } -CTAVLNilNode >> remove: anObject path: list [ - ^ nil + ^ 0 ] - -{ #category : 'accessing' } -CTAVLNilNode >> withAllChildren: aCollection [ - "Do nothing for nil node" -] \ No newline at end of file diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index a7c682f..38d5ea7 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -21,7 +21,8 @@ Class { #instVars : [ 'left', 'contents', - 'right' + 'right', + 'height' ], #category : 'Containers-AVL-Tree', #package : 'Containers-AVL-Tree' @@ -36,300 +37,67 @@ CTAVLNode class >> with: anInteger [ yourself ] -{ #category : 'adding' } -CTAVLNode >> add: anInteger path: list [ - anInteger < contents ifTrue: [ - list add: false -> left. - left := left addChild: anInteger. - ] ifFalse: [ - list add: true -> right. - right := right addChild: anInteger. - ] -] - -{ #category : 'adding' } -CTAVLNode >> addChild: newObject [ - | path | - path := OrderedCollection with: nil -> self. - self add: newObject path: path. - self checkPath: path. - ^ self -] - -{ #category : 'private' } -CTAVLNode >> balance: index path: aCollection [ - | x y z a b c | - z := aCollection at: index. - y := aCollection at: index + 1. - x := aCollection at: index + 2. - c := z value. - b := y value. - a := x value. - (y key and: [ x key ]) ifTrue: [ - ^ self rrrotationZ: c y: b x: a ]. - (y key not and: [ x key not ]) ifTrue: [ - ^ self llrotationZ: c y: b x: a ]. - (y key not and: [ x key ]) ifTrue: [ - ^ self lrrotationZ: c y: b x: a ]. - "(y key and: [ x key not ])" - ^ self rlrotationZ: c y: b x: a. -] - -{ #category : 'private' } -CTAVLNode >> balanceZ: z y: y x: x [ - | a b c | - c := z value. - b := y value. - a := x value. - (y key and: [ x key ]) ifTrue: [ - ^ self rrrotationZ: c y: b x: a ]. - (y key not and: [ x key not ]) ifTrue: [ - ^ self llrotationZ: c y: b x: a ]. - (y key not and: [ x key ]) ifTrue: [ - ^ self lrrotationZ: c y: b x: a ]. - "(y key and: [ x key not ])" - ^ self rlrotationZ: c y: b x: a. -] - -{ #category : 'private' } -CTAVLNode >> checkPath: aCollection [ - aCollection size < 3 ifTrue: [ ^ self ]. - (1 to: aCollection size - 2) reverseDo: [ :index | - | assoc | - assoc := aCollection at: index. - assoc value isBalanced ifFalse: [ | z y x | - z := aCollection at: index. - y := aCollection at: index + 1. - x := aCollection at: index + 2. - ^ self balanceZ: z y: y x: x ] ] -] - -{ #category : 'private' } -CTAVLNode >> checkRemovingPath: path [ - path reverseDo: [ :node | - node isBalanced ifFalse: [ | z y x | - z := node. - y := z largerNode. - x := y value largerNode. - self balanceZ: node y: y x: x ] ]. -] - -{ #category : 'accessing' } -CTAVLNode >> children [ - ^ { left. right } -] - -{ #category : 'accessing' } -CTAVLNode >> allChildren [ - "Collects all child nodes recursively." - | children | - children := OrderedCollection new. - self left ifNotNil: [children addAll: self left allChildren]. - self right ifNotNil: [children addAll: self right allChildren]. - children add: self. - ^ children. -] - -{ #category : 'enumerating' } -CTAVLNode >> childrenDo: aBlock [ - left childrenDo: aBlock. - right childrenDo: aBlock. -] - { #category : 'accessing' } CTAVLNode >> contents [ + ^ contents ] { #category : 'accessing' } -CTAVLNode >> contents: anInteger [ - contents := anInteger -] - -{ #category : 'enumerating' } -CTAVLNode >> do: aBlock [ - left do: aBlock. - aBlock value: self contents. - right do: aBlock. -] +CTAVLNode >> contents: anObject [ -{ #category : 'testing' } -CTAVLNode >> hasNoChildren [ - ^ left nodeSize = 0 and: [ right nodeSize = 0 ] + contents := anObject ] { #category : 'accessing' } CTAVLNode >> height [ - ^ (left height max: right height) + 1 + + ^ height ] -{ #category : 'testing' } -CTAVLNode >> isBalanced [ - ^ (left height - right height) abs <= 1 +{ #category : 'initialization' } +CTAVLNode >> initialize [ + + super initialize. + left := CTAVLNilNode new parent: self. + right := CTAVLNilNode new parent: self. + height := 1 ] { #category : 'testing' } -CTAVLNode >> isTotalBalanced [ - ^ self isBalanced - and: [ left isTotalBalanced - and: [ right isTotalBalanced ] ] -] +CTAVLNode >> isEmpty [ -{ #category : 'accessing' } -CTAVLNode >> largerNode [ - | size1 size2 isLeft | - size1 := left height. - size2 := right height. - isLeft := size1 > size2. - ^ isLeft not -> (isLeft ifTrue: [ left ] ifFalse: [ right ]) + ^ false ] { #category : 'accessing' } CTAVLNode >> left [ - ^ left -] - -{ #category : 'accessing' } -CTAVLNode >> left: aNode [ - left := aNode. - aNode parent: self -] - -{ #category : 'private' } -CTAVLNode >> llrotationZ: z y: y x: x [ - | a3 a4 new | - a3 := y right. - a4 := z right. - - new := self class with: z contents. - new left: a3; right: a4. - z left: x; contents: y contents; right: new. -] -{ #category : 'private' } -CTAVLNode >> lrrotationZ: z y: y x: x [ - | a1 a2 a3 new | - a1 := y left. - a2 := x left. - a3 := x right. - new := self class with: y contents. - new left: a1; right: a2. - y contents: x contents; left: new; right: a3. - - self llrotationZ: z y: y x: new + ^ left ] { #category : 'accessing' } -CTAVLNode >> nodeSize [ - ^ 1 + left nodeSize + right nodeSize -] - -{ #category : 'printing' } -CTAVLNode >> printOn: stream [ - contents printOn: stream -] +CTAVLNode >> left: aNode [ -{ #category : 'removing' } -CTAVLNode >> remove: anObject path: list [ - contents = anObject ifTrue: [ - ^ self - ] ifFalse: [ - | node nodeToRemove isLeft | - isLeft := anObject < contents. - node := isLeft ifTrue: [ left ] ifFalse: [ right ]. - list add: self. - nodeToRemove := node remove: anObject path: list. - nodeToRemove == node ifTrue: [ - | successor | - successor := node successor: list. - isLeft - ifTrue: [ left := successor ] - ifFalse: [ right := successor ] - ]. - ^ nodeToRemove - ]. -] - -{ #category : 'removing' } -CTAVLNode >> removeMinimum: list [ - | res | - left nodeSize = 0 ifTrue: [ - res := self class with: contents. - contents := right contents. - left := right left. - right := right right ] - ifFalse: [ - list add: self. - left hasNoChildren ifTrue: [ - res := left. - left := CTAVLNilNode new ] - ifFalse: [ res := left removeMinimum: list ] ]. - ^ res + left := aNode. + aNode ifNotNil: [ aNode parent: self ] ] { #category : 'accessing' } CTAVLNode >> right [ + ^ right ] { #category : 'accessing' } -CTAVLNode >> right: aNode [ - right := aNode. - aNode parent: self -] +CTAVLNode >> right: aNode [ -{ #category : 'private' } -CTAVLNode >> rlrotationZ: z y: y x: x [ - | a1 a2 a3 a4 new | - a1 := z left. - a2 := x left. - a3 := x right. - a4 := y right. - new := self class with: y contents. - new left: a3; right: a4. - y contents: x contents; left: a2; right: new. - self rrrotationZ: z y: y x: new -] - -{ #category : 'private' } -CTAVLNode >> rrrotationZ: z y: y x: x [ - | a1 a2 new | - a1 := z left. - a2 := y left. - - new := self class with: z contents. - new left: a1; right: a2. - z left: new; right: x; contents: y contents + right := aNode. + aNode ifNotNil: [ aNode parent: self ] ] -{ #category : 'search' } -CTAVLNode >> search: anInteger [ - contents = anInteger ifTrue: [ ^ contents ]. - ^ (anInteger < contents ifTrue: [ left ] ifFalse: [ right ]) search: anInteger -] +{ #category : 'accessing' } +CTAVLNode >> size [ -{ #category : 'removing' } -CTAVLNode >> successor: list [ - ^ self hasNoChildren - ifTrue: [ CTAVLNilNode new ] - ifFalse: [ - (left nodeSize > 0 and: [ right nodeSize > 0 ]) ifTrue: [ - right hasNoChildren - ifTrue: [ list add: (right left: left; yourself) ] - ifFalse: [ | min newList | - newList := OrderedCollection new. - min := right removeMinimum: newList. - list add: min. - list addAll: newList. - min left: left. - min right: right. - right := min ] - ] ifFalse: [ - list add: (left nodeSize = 0 ifTrue: [ right ] ifFalse: [ left ]) ] ] + ^ 1 + left size + right size ] - -{ #category : 'accessing' } -CTAVLNode >> withAllChildren: aCollection [ - aCollection add: self. - self childrenDo: [ :child | child withAllChildren: aCollection ] -] \ No newline at end of file diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index 705a8be..b4c4e7b 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -23,7 +23,7 @@ Date: October 20, 2023 " Class { #name : 'CTAVLTree', - #superclass : 'Collection', + #superclass : 'Object', #instVars : [ 'root' ], @@ -31,81 +31,32 @@ Class { #package : 'Containers-AVL-Tree' } -{ #category : 'adding' } -CTAVLTree >> add: newObject [ - root := root addChild: newObject. - ^ newObject -] - -{ #category : 'enumerating' } -CTAVLTree >> do: aBlock [ - root do: aBlock -] +{ #category : 'removing' } +CTAVLTree >> clear [ -{ #category : 'testing' } -CTAVLTree >> hasNoChildren [ - ^ root nodeSize = 0 + root := CTAVLNilNode new ] -{ #category : 'public' } +{ #category : 'accessing' } CTAVLTree >> height [ - ^ root height -] -{ #category : 'testing' } -CTAVLTree >> includes: anObject [ - anObject ifNil: [ ^ false ]. - ^ (self search: anObject) notNil + ^ root height ] { #category : 'initialization' } CTAVLTree >> initialize [ - super initialize. - root := CTAVLNilNode new. -] -{ #category : 'testing' } -CTAVLTree >> isBalanced [ - ^ root isBalanced + super initialize. + root := CTAVLNilNode new ] { #category : 'testing' } -CTAVLTree >> isTotalBalanced [ - ^ root isTotalBalanced -] - -{ #category : 'removing' } -CTAVLTree >> remove: oldObject ifAbsent: anExceptionBlock [ - | toRemove path | - path := OrderedCollection new. - toRemove := root remove: oldObject path: path. - toRemove ifNil: [ ^ anExceptionBlock value ]. - - toRemove == root ifTrue: [ - root := root successor: path. - root ifNil: [ root := CTAVLNilNode new ] ]. - root checkRemovingPath: path. - - ^ toRemove contents -] +CTAVLTree >> isEmpty [ -{ #category : 'search' } -CTAVLTree >> search: anInteger [ - ^ root search: anInteger + ^ root isEmpty ] { #category : 'accessing' } -CTAVLTree >> size [ - ^ root nodeSize +CTAVLTree >> size [ + ^ root size ] - -{ #category : 'accessing' } -CTAVLTree >> root [ - ^ root -] - -{ #category : 'accessing' } -CTAVLTree >> allChildren [ - "Returns a collection of all child nodes in the tree." - ^ root ifNil: [#()] ifNotNil: [root allChildren]. -] \ No newline at end of file From f161b6397b126eb93b2a094c27b3b760f42c7de1 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 1 Aug 2025 01:07:01 +0530 Subject: [PATCH 02/10] Added basic BST Insertion functionality --- .../CTAVLTreeTest.class.st | 23 ++++++++++++++++ .../CTAVLAbstractNode.class.st | 24 +++++++++++++++++ src/Containers-AVL-Tree/CTAVLNilNode.class.st | 26 +++++++++++++++++++ src/Containers-AVL-Tree/CTAVLNode.class.st | 16 ++++++++++++ src/Containers-AVL-Tree/CTAVLTree.class.st | 21 +++++++++++++++ 5 files changed, 110 insertions(+) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index c25a2dd..307b565 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -18,6 +18,22 @@ CTAVLTreeTest >> setUp [ tree := CTAVLTree new ] +{ #category : 'tests' } +CTAVLTreeTest >> testAddMultipleElements [ + + tree addAll: #(50 30 70). + self assert: tree size equals: 3 +] + +{ #category : 'tests' } +CTAVLTreeTest >> testAddSingleElement [ + + tree add: 42. + self deny: tree isEmpty. + self assert: tree size equals: 1. + self assert: tree height equals: 1 +] + { #category : 'tests' } CTAVLTreeTest >> testClear [ @@ -26,6 +42,13 @@ CTAVLTreeTest >> testClear [ self assert: tree size equals: 0 ] +{ #category : 'tests' } +CTAVLTreeTest >> testDuplicateHandling [ + + tree addAll: #(42 42 50). + self assert: tree size equals: 2 +] + { #category : 'tests' } CTAVLTreeTest >> testEmpty [ diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index eef439b..4d96199 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -22,6 +22,24 @@ Class { #package : 'Containers-AVL-Tree' } +{ #category : 'adding' } +CTAVLAbstractNode >> addChild: anObject [ + + ^ self subclassResponsibility +] + +{ #category : 'accessing' } +CTAVLAbstractNode >> contents [ + + ^ self subclassResponsibility +] + +{ #category : 'accessing' } +CTAVLAbstractNode >> contents: anObject [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAVLAbstractNode >> height [ @@ -34,6 +52,12 @@ CTAVLAbstractNode >> isEmpty [ ^ self subclassResponsibility ] +{ #category : 'testing' } +CTAVLAbstractNode >> isLeaf [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAVLAbstractNode >> parent [ diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index a4911cf..8956147 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -13,6 +13,27 @@ Class { #package : 'Containers-AVL-Tree' } +{ #category : 'adding' } +CTAVLNilNode >> addChild: anObject [ + + ^ CTAVLNode new + contents: anObject; + parent: self parent; + yourself +] + +{ #category : 'accessing' } +CTAVLNilNode >> contents [ + + ^ nil +] + +{ #category : 'accessing' } +CTAVLNilNode >> contents: anObject [ + + "Do nothing for nil node" +] + { #category : 'accessing' } CTAVLNilNode >> height [ @@ -25,6 +46,11 @@ CTAVLNilNode >> isEmpty [ ^ true ] +{ #category : 'testing' } +CTAVLNilNode >> isLeaf [ + ^ false +] + { #category : 'accessing' } CTAVLNilNode >> size [ diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index 38d5ea7..02c6592 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -37,6 +37,17 @@ CTAVLNode class >> with: anInteger [ yourself ] +{ #category : 'adding' } +CTAVLNode >> addChild: anObject [ + + anObject < contents + ifTrue: [ self left: (left addChild: anObject) ] + ifFalse: [ + anObject > contents + ifTrue: [ self right: (right addChild: anObject) ] ]. + ^ self +] + { #category : 'accessing' } CTAVLNode >> contents [ @@ -70,6 +81,11 @@ CTAVLNode >> isEmpty [ ^ false ] +{ #category : 'testing' } +CTAVLNode >> isLeaf [ + ^ left isEmpty and: [ right isEmpty ] +] + { #category : 'accessing' } CTAVLNode >> left [ diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index b4c4e7b..54a8e07 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -31,6 +31,21 @@ Class { #package : 'Containers-AVL-Tree' } +{ #category : 'adding' } +CTAVLTree >> add: anObject [ + + root := root addChild: anObject. + root parent: nil. + ^ anObject +] + +{ #category : 'adding' } +CTAVLTree >> addAll: aCollection [ + + aCollection do: [ :each | self add: each ]. + ^ aCollection +] + { #category : 'removing' } CTAVLTree >> clear [ @@ -56,6 +71,12 @@ CTAVLTree >> isEmpty [ ^ root isEmpty ] +{ #category : 'accessing' } +CTAVLTree >> root [ + + ^ root isEmpty ifTrue: [ nil ] ifFalse: [ root ] +] + { #category : 'accessing' } CTAVLTree >> size [ ^ root size From 19553a33fbb7a2d8760fe0c0b7eba57919bfaf89 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 1 Aug 2025 11:51:23 +0530 Subject: [PATCH 03/10] Add search functionality and tree traversal operations --- .../CTAVLTreeTest.class.st | 52 +++++++++++++++++++ .../CTAVLAbstractNode.class.st | 30 +++++++++++ src/Containers-AVL-Tree/CTAVLNilNode.class.st | 30 +++++++++++ src/Containers-AVL-Tree/CTAVLNode.class.st | 45 ++++++++++++++++ src/Containers-AVL-Tree/CTAVLTree.class.st | 44 ++++++++++++++++ 5 files changed, 201 insertions(+) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index 307b565..b2535a8 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -34,6 +34,13 @@ CTAVLTreeTest >> testAddSingleElement [ self assert: tree height equals: 1 ] +{ #category : 'tests' } +CTAVLTreeTest >> testAsArray [ + + tree addAll: #(50 30 70 20 40). + self assert: tree asArray equals: #(20 30 40 50 70) +] + { #category : 'tests' } CTAVLTreeTest >> testClear [ @@ -56,3 +63,48 @@ CTAVLTreeTest >> testEmpty [ self assert: tree size equals: 0. self assert: tree height equals: 0 ] + +{ #category : 'tests' } +CTAVLTreeTest >> testFindMinMax [ + + self assert: tree findMin isNil. + self assert: tree findMax isNil. + + tree addAll: #(50 30 70 20 80). + self assert: tree findMin equals: 20. + self assert: tree findMax equals: 80 +] + +{ #category : 'tests' } +CTAVLTreeTest >> testHeight [ + + self assert: tree height equals: 0. + + tree add: 50. + self assert: tree height equals: 1. + + tree addAll: #(30 70). + self assert: tree height equals: 2 +] + +{ #category : 'tests' } +CTAVLTreeTest >> testInOrderTraversal [ + + | 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) +] + +{ #category : 'tests' } +CTAVLTreeTest >> testIncludes [ + + tree addAll: #(50 30 70 20 40). + + self assert: (tree includes: 50). + self assert: (tree includes: 30). + self deny: (tree includes: 99) +] diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index 4d96199..8937ee9 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -28,6 +28,12 @@ CTAVLAbstractNode >> addChild: anObject [ ^ self subclassResponsibility ] +{ #category : 'accessing' } +CTAVLAbstractNode >> balanceFactor [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAVLAbstractNode >> contents [ @@ -40,12 +46,30 @@ CTAVLAbstractNode >> contents: anObject [ ^ self subclassResponsibility ] +{ #category : 'searching' } +CTAVLAbstractNode >> findMax [ + + ^ self subclassResponsibility +] + +{ #category : 'searching' } +CTAVLAbstractNode >> findMin [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAVLAbstractNode >> height [ ^ self subclassResponsibility ] +{ #category : 'enumerating' } +CTAVLAbstractNode >> inOrderDo: aBlock [ + + ^ self subclassResponsibility +] + { #category : 'testing' } CTAVLAbstractNode >> isEmpty [ @@ -70,6 +94,12 @@ CTAVLAbstractNode >> parent: aNode [ parent := aNode ] +{ #category : 'accessing' } +CTAVLAbstractNode >> search: anObject [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAVLAbstractNode >> size [ diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index 8956147..5a07cfc 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -22,6 +22,12 @@ CTAVLNilNode >> addChild: anObject [ yourself ] +{ #category : 'accessing' } +CTAVLNilNode >> balanceFactor [ + + ^ 0 +] + { #category : 'accessing' } CTAVLNilNode >> contents [ @@ -34,12 +40,30 @@ CTAVLNilNode >> contents: anObject [ "Do nothing for nil node" ] +{ #category : 'searching' } +CTAVLNilNode >> findMax [ + + ^ nil +] + +{ #category : 'searching' } +CTAVLNilNode >> findMin [ + + ^ nil +] + { #category : 'accessing' } CTAVLNilNode >> height [ ^ 0 ] +{ #category : 'enumerating' } +CTAVLNilNode >> inOrderDo: aBlock [ + + "Do Nothing for Nil Node" +] + { #category : 'testing' } CTAVLNilNode >> isEmpty [ @@ -51,6 +75,12 @@ CTAVLNilNode >> isLeaf [ ^ false ] +{ #category : 'accessing' } +CTAVLNilNode >> search: anObject [ + + ^ nil +] + { #category : 'accessing' } CTAVLNilNode >> size [ diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index 02c6592..28b5569 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -48,6 +48,12 @@ CTAVLNode >> addChild: anObject [ ^ self ] +{ #category : 'accessing' } +CTAVLNode >> balanceFactor [ + + ^ left height - right height +] + { #category : 'accessing' } CTAVLNode >> contents [ @@ -60,12 +66,36 @@ CTAVLNode >> contents: anObject [ contents := anObject ] +{ #category : 'searching' } +CTAVLNode >> findMax [ + + ^ right isEmpty + ifTrue: [ contents ] + ifFalse: [ right findMax ] +] + +{ #category : 'searching' } +CTAVLNode >> findMin [ + + ^ left isEmpty + ifTrue: [ contents ] + ifFalse: [ left findMin ] +] + { #category : 'accessing' } CTAVLNode >> height [ ^ height ] +{ #category : 'enumerating' } +CTAVLNode >> inOrderDo: aBlock [ + + left inOrderDo: aBlock. + aBlock value: contents. + right inOrderDo: aBlock +] + { #category : 'initialization' } CTAVLNode >> initialize [ @@ -112,8 +142,23 @@ CTAVLNode >> right: aNode [ aNode ifNotNil: [ aNode parent: self ] ] +{ #category : 'accessing' } +CTAVLNode >> search: anObject [ + + contents = anObject ifTrue: [ ^ contents ]. + ^ anObject < contents + ifTrue: [ left search: anObject ] + ifFalse: [ right search: anObject ] +] + { #category : 'accessing' } CTAVLNode >> size [ ^ 1 + left size + right size ] + +{ #category : 'private' } +CTAVLNode >> updateHeight [ + +height := 1 + (left height max: right height) +] diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index 54a8e07..0ea1147 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -46,18 +46,62 @@ CTAVLTree >> addAll: aCollection [ ^ aCollection ] +{ #category : 'converting' } +CTAVLTree >> asArray [ + + | result | + result := OrderedCollection new: self size. + self inOrderDo: [ :each | result add: each ]. + ^ result asArray +] + { #category : 'removing' } CTAVLTree >> clear [ root := CTAVLNilNode new ] +{ #category : 'enumerating' } +CTAVLTree >> do: aBlock [ + + "Alias for inOrderDo: - visits elements in sorted order" + self inOrderDo: aBlock +] + +{ #category : 'searching' } +CTAVLTree >> findMax [ + + ^ self isEmpty + ifTrue: [ nil ] + ifFalse: [ root findMax ] +] + +{ #category : 'searching' } +CTAVLTree >> findMin [ + + ^ self isEmpty + ifTrue: [ nil ] + ifFalse: [ root findMin ] +] + { #category : 'accessing' } CTAVLTree >> height [ ^ root height ] +{ #category : 'enumerating' } +CTAVLTree >> inOrderDo: aBlock [ + + root inOrderDo: aBlock +] + +{ #category : 'testing' } +CTAVLTree >> includes: anObject [ + + ^ (root search: anObject) notNil +] + { #category : 'initialization' } CTAVLTree >> initialize [ From 7d3e76f36e74aa59a79b6a7dc1353b4609db999a Mon Sep 17 00:00:00 2001 From: Alokzh Date: Fri, 1 Aug 2025 23:36:28 +0530 Subject: [PATCH 04/10] Added rotation operations & validation logic --- .../CTAVLTreeTest.class.st | 57 +++++++++++++++ .../CTAVLAbstractNode.class.st | 12 ++++ src/Containers-AVL-Tree/CTAVLNilNode.class.st | 12 ++++ src/Containers-AVL-Tree/CTAVLNode.class.st | 72 ++++++++++++++++++- src/Containers-AVL-Tree/CTAVLTree.class.st | 7 ++ 5 files changed, 159 insertions(+), 1 deletion(-) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index b2535a8..9df903d 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -108,3 +108,60 @@ CTAVLTreeTest >> testIncludes [ self assert: (tree includes: 30). self deny: (tree includes: 99) ] + +{ #category : 'tests' } +CTAVLTreeTest >> testLLRotation [ + + tree add: 3. + tree add: 2. + tree add: 1. + + self assert: tree root contents equals: 2. + self assert: tree asArray equals: #(1 2 3) +] + +{ #category : 'tests' } +CTAVLTreeTest >> testLRRotation [ + + tree add: 3. + tree add: 1. + tree add: 2. + + self assert: tree root contents equals: 2. + self assert: tree asArray equals: #(1 2 3) +] + +{ #category : 'tests' } +CTAVLTreeTest >> testRLRotation [ + + tree add: 1. + tree add: 3. + tree add: 2. + + self assert: tree root contents equals: 2. + self assert: tree asArray equals: #(1 2 3) +] + +{ #category : 'tests' } +CTAVLTreeTest >> testRRRotation [ + + tree add: 1. + tree add: 2. + tree add: 3. + + self assert: tree root contents equals: 2. + self assert: tree asArray equals: #(1 2 3) +] + +{ #category : 'tests' } +CTAVLTreeTest >> testValidation [ + + self assert: tree validate. + + tree addAll: #(50 30 70 20 40). + self assert: tree validate. + + "Test after operations" + tree add: 60. + self assert: tree validate +] diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index 8937ee9..bd51d7c 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -105,3 +105,15 @@ CTAVLAbstractNode >> size [ ^ self subclassResponsibility ] + +{ #category : 'validation' } +CTAVLAbstractNode >> validateAsRoot [ + + ^ self subclassResponsibility +] + +{ #category : 'validation' } +CTAVLAbstractNode >> validateWithMin: min max: max [ + + ^ self subclassResponsibility +] diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index 5a07cfc..0525b7e 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -86,3 +86,15 @@ CTAVLNilNode >> size [ ^ 0 ] + +{ #category : 'validation' } +CTAVLNilNode >> validateAsRoot [ + + ^ true +] + +{ #category : 'validation' } +CTAVLNilNode >> validateWithMin: min max: max [ + + ^ true +] diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index 28b5569..1f28f36 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -45,7 +45,7 @@ CTAVLNode >> addChild: anObject [ ifFalse: [ anObject > contents ifTrue: [ self right: (right addChild: anObject) ] ]. - ^ self + ^ self updateHeightAndRebalance ] { #category : 'accessing' } @@ -129,6 +129,24 @@ CTAVLNode >> left: aNode [ aNode ifNotNil: [ aNode parent: self ] ] +{ #category : 'private' } +CTAVLNode >> rebalance [ + + | bf | + bf := self balanceFactor. + "Left-heavy case" + bf > 1 ifTrue: [ + "Left-Right case" + (left balanceFactor < 0) ifTrue: [ self left: left rotateLeft ]. + ^ self rotateRight ]. + "Right-heavy case" + bf < -1 ifTrue: [ + "Right-Left case" + (right balanceFactor > 0) ifTrue: [ self right: right rotateRight ]. + ^ self rotateLeft ]. + ^ self "Already balanced" +] + { #category : 'accessing' } CTAVLNode >> right [ @@ -142,6 +160,30 @@ CTAVLNode >> right: aNode [ aNode ifNotNil: [ aNode parent: self ] ] +{ #category : 'private' } +CTAVLNode >> rotateLeft [ + + | newRoot | + newRoot := right. + self right: newRoot left. + newRoot left: self. + self updateHeight. + newRoot updateHeight. + ^ newRoot +] + +{ #category : 'private' } +CTAVLNode >> rotateRight [ + + | newRoot | + newRoot := left. + self left: newRoot right. + newRoot right: self. + self updateHeight. + newRoot updateHeight. + ^ newRoot +] + { #category : 'accessing' } CTAVLNode >> search: anObject [ @@ -162,3 +204,31 @@ CTAVLNode >> updateHeight [ height := 1 + (left height max: right height) ] + +{ #category : 'private' } +CTAVLNode >> updateHeightAndRebalance [ + + self updateHeight. + ^ self rebalance +] + +{ #category : 'validation' } +CTAVLNode >> validateAsRoot [ + + ^ self validateWithMin: nil max: nil +] + +{ #category : 'validation' } +CTAVLNode >> validateWithMin: min max: max [ + + "Check BST property" + (min notNil and: [ contents < min ]) ifTrue: [ ^ false ]. + (max notNil and: [ contents > max ]) ifTrue: [ ^ false ]. + "Check AVL property" + (self balanceFactor abs > 1) ifTrue: [ ^ false ]. + "Check height property" + (self height = (1 + (left height max: right height))) ifFalse: [ ^ false ]. + "Recursively validate children" + ^ (left validateWithMin: min max: contents) and: [ + right validateWithMin: contents max: max ] +] diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index 0ea1147..d2f1e1c 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -125,3 +125,10 @@ CTAVLTree >> root [ CTAVLTree >> size [ ^ root size ] + +{ #category : 'accessing' } +CTAVLTree >> validate [ + + "Validate that the tree maintains both BST and AVL properties" + ^ root validateAsRoot +] From ef008ab20bec893c8346f3697ae6fc53b17906bf Mon Sep 17 00:00:00 2001 From: Alokzh Date: Sat, 2 Aug 2025 10:40:44 +0530 Subject: [PATCH 05/10] Implement node removal & remaining traversal methods --- .../CTAVLTreeTest.class.st | 87 +++++++++++++++++++ .../CTAVLAbstractNode.class.st | 18 ++++ src/Containers-AVL-Tree/CTAVLNilNode.class.st | 18 ++++ src/Containers-AVL-Tree/CTAVLNode.class.st | 44 ++++++++++ src/Containers-AVL-Tree/CTAVLTree.class.st | 29 +++++++ 5 files changed, 196 insertions(+) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index 9df903d..ae03362 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -49,6 +49,18 @@ CTAVLTreeTest >> testClear [ self assert: tree size equals: 0 ] +{ #category : 'tests' } +CTAVLTreeTest >> 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' } CTAVLTreeTest >> testDuplicateHandling [ @@ -131,6 +143,26 @@ CTAVLTreeTest >> testLRRotation [ self assert: tree asArray equals: #(1 2 3) ] +{ #category : 'tests' } +CTAVLTreeTest >> testPostOrderTraversal [ + + | result | + tree addAll: #(50 30 70 20). + result := OrderedCollection new. + tree postOrderDo: [ :each | result add: each ]. + self assert: result last equals: tree root contents +] + +{ #category : 'tests' } +CTAVLTreeTest >> testPreOrderTraversal [ + + | result | + tree addAll: #(50 30 70 20). + result := OrderedCollection new. + tree preOrderDo: [ :each | result add: each ]. + self assert: result first equals: tree root contents +] + { #category : 'tests' } CTAVLTreeTest >> testRLRotation [ @@ -153,6 +185,61 @@ CTAVLTreeTest >> testRRRotation [ self assert: tree asArray equals: #(1 2 3) ] +{ #category : 'tests' } +CTAVLTreeTest >> testRemoveLeafNode [ + + tree addAll: #(50 30 70 20). + + tree remove: 20. + self assert: tree size equals: 3. + self deny: (tree includes: 20). + self assert: tree validate +] + +{ #category : 'tests' } +CTAVLTreeTest >> 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). + self assert: tree validate +] + +{ #category : 'tests' } +CTAVLTreeTest >> testRemoveNodeWithTwoChildren [ + + tree addAll: #(50 30 70 20 40). + + tree remove: 30. + self assert: tree size equals: 4. + self deny: (tree includes: 30). + self assert: tree validate +] + +{ #category : 'tests' } +CTAVLTreeTest >> 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' } +CTAVLTreeTest >> testRemoveRoot [ + + tree addAll: #(50 30 70 20 40 60 80). + tree remove: 50. + self assert: tree size equals: 6. + self deny: (tree includes: 50). + self assert: tree validate +] + { #category : 'tests' } CTAVLTreeTest >> testValidation [ diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index bd51d7c..57228a0 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -94,6 +94,24 @@ CTAVLAbstractNode >> parent: aNode [ parent := aNode ] +{ #category : 'enumerating' } +CTAVLAbstractNode >> postOrderDo: aBlock [ + + ^ self subclassResponsibility +] + +{ #category : 'enumerating' } +CTAVLAbstractNode >> preOrderDo: aBlock [ + + ^ self subclassResponsibility +] + +{ #category : 'removing' } +CTAVLAbstractNode >> removeValue: anObject [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAVLAbstractNode >> search: anObject [ diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index 0525b7e..9a94b2c 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -75,6 +75,24 @@ CTAVLNilNode >> isLeaf [ ^ false ] +{ #category : 'enumerating' } +CTAVLNilNode >> postOrderDo: aBlock [ + + "Do Nothing for Nil Node" +] + +{ #category : 'enumerating' } +CTAVLNilNode >> preOrderDo: aBlock [ + + "Do Nothing for Nil Node" +] + +{ #category : 'removing' } +CTAVLNilNode >> removeValue: anObject [ + + ^ self +] + { #category : 'accessing' } CTAVLNilNode >> search: anObject [ diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index 1f28f36..52bf987 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -129,6 +129,22 @@ CTAVLNode >> left: aNode [ aNode ifNotNil: [ aNode parent: self ] ] +{ #category : 'enumerating' } +CTAVLNode >> postOrderDo: aBlock [ + + left postOrderDo: aBlock. + right postOrderDo: aBlock. + aBlock value: contents +] + +{ #category : 'enumerating' } +CTAVLNode >> preOrderDo: aBlock [ + + aBlock value: contents. + left preOrderDo: aBlock. + right preOrderDo: aBlock +] + { #category : 'private' } CTAVLNode >> rebalance [ @@ -147,6 +163,34 @@ CTAVLNode >> rebalance [ ^ self "Already balanced" ] +{ #category : 'private' } +CTAVLNode >> removeThisNode [ + + "Handle removal of the current node, then rebalance." + | successorValue | + "Case 1 & 2: Leaf or one child" + left isEmpty ifTrue: [ ^ right ]. + right isEmpty ifTrue: [ ^ left ]. + "Case 3: Two children" + successorValue := right findMin. + self contents: successorValue. + self right: (right removeValue: successorValue). + ^ self updateHeightAndRebalance +] + +{ #category : 'removing' } +CTAVLNode >> removeValue: anObject [ + + anObject < contents ifTrue: [ + self left: (left removeValue: anObject). + ^ self updateHeightAndRebalance ]. + anObject > contents ifTrue: [ + self right: (right removeValue: anObject). + ^ self updateHeightAndRebalance ]. + "Found the node to remove" + ^ self removeThisNode +] + { #category : 'accessing' } CTAVLNode >> right [ diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index d2f1e1c..6028a52 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -115,6 +115,35 @@ CTAVLTree >> isEmpty [ ^ root isEmpty ] +{ #category : 'enumerating' } +CTAVLTree >> postOrderDo: aBlock [ + + root postOrderDo: aBlock +] + +{ #category : 'enumerating' } +CTAVLTree >> preOrderDo: aBlock [ + + root preOrderDo: aBlock +] + +{ #category : 'removing' } +CTAVLTree >> remove: anObject [ + + ^ self + remove: anObject + ifAbsent: [ NotFound signalFor: anObject in: self ] +] + +{ #category : 'removing' } +CTAVLTree >> remove: anObject ifAbsent: aBlock [ + + (self includes: anObject) ifFalse: [ ^ aBlock value ]. + root := root removeValue: anObject. + root parent: nil. + ^ anObject +] + { #category : 'accessing' } CTAVLTree >> root [ From 1dd89dc4b8f83edbd9350a044a71c56655d0f5c3 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Sat, 2 Aug 2025 18:11:07 +0530 Subject: [PATCH 06/10] Implemented collection protocol & search methods --- .../CTAVLTreeTest.class.st | 67 +++++++++++++++++ .../CTAVLAbstractNode.class.st | 12 ++++ src/Containers-AVL-Tree/CTAVLNilNode.class.st | 12 ++++ src/Containers-AVL-Tree/CTAVLNode.class.st | 16 +++++ src/Containers-AVL-Tree/CTAVLTree.class.st | 72 +++++++++++++++++++ 5 files changed, 179 insertions(+) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index ae03362..9dbf00d 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -34,6 +34,15 @@ CTAVLTreeTest >> testAddSingleElement [ self assert: tree height equals: 1 ] +{ #category : 'tests' } +CTAVLTreeTest >> testAnySatisfy [ + + tree addAll: #(50 30 70 20 40). + + self assert: (tree anySatisfy: [ :each | each > 60 ]). + self deny: (tree anySatisfy: [ :each | each > 100 ]) +] + { #category : 'tests' } CTAVLTreeTest >> testAsArray [ @@ -41,6 +50,14 @@ CTAVLTreeTest >> testAsArray [ self assert: tree asArray equals: #(20 30 40 50 70) ] +{ #category : 'tests' } +CTAVLTreeTest >> testAtIfAbsent [ + + tree addAll: #(50 30 70). + self assert: (tree at: 30 ifAbsent: [ #notFound ]) equals: 30. + self assert: (tree at: 99 ifAbsent: [ #notFound ]) equals: #notFound +] + { #category : 'tests' } CTAVLTreeTest >> testClear [ @@ -49,6 +66,29 @@ CTAVLTreeTest >> testClear [ self assert: tree size equals: 0 ] +{ #category : 'tests' } +CTAVLTreeTest >> testCollect [ + + | doubled | + tree addAll: #(50 30 70 20 40). + + doubled := tree collect: [ :each | each * 2 ]. + self assert: doubled asArray equals: #(40 60 80 100 140) +] + +{ #category : 'tests' } +CTAVLTreeTest >> testDetect [ + + | found | + tree addAll: #(50 30 70 20 40). + + found := tree detect: [ :each | each > 45 ] ifNone: [ nil ]. + self assert: found equals: 50. + + found := tree detect: [ :each | each > 100 ] ifNone: [ #none ]. + self assert: found equals: #none +] + { #category : 'tests' } CTAVLTreeTest >> testDoMethod [ @@ -87,6 +127,23 @@ CTAVLTreeTest >> testFindMinMax [ self assert: tree findMax equals: 80 ] +{ #category : 'tests' } +CTAVLTreeTest >> testFindMinMaxNode [ + + tree addAll: #(50 30 70 20 80). + + self assert: tree findMinNode contents equals: 20. + self assert: tree findMaxNode contents equals: 80 +] + +{ #category : 'tests' } +CTAVLTreeTest >> testFirstLast [ + + tree addAll: #(50 30 70 20 80). + self assert: tree first equals: 20. + self assert: tree last equals: 80 +] + { #category : 'tests' } CTAVLTreeTest >> testHeight [ @@ -240,6 +297,16 @@ CTAVLTreeTest >> testRemoveRoot [ self assert: tree validate ] +{ #category : 'tests' } +CTAVLTreeTest >> testSelect [ + + | evens | + tree addAll: #(50 30 70 20 40). + + evens := tree select: [ :each | each even ]. + self assert: evens asArray equals: #(20 30 40 50 70) +] + { #category : 'tests' } CTAVLTreeTest >> testValidation [ diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index 57228a0..81bb861 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -52,12 +52,24 @@ CTAVLAbstractNode >> findMax [ ^ self subclassResponsibility ] +{ #category : 'searching' } +CTAVLAbstractNode >> findMaxNode [ + + ^ self subclassResponsibility +] + { #category : 'searching' } CTAVLAbstractNode >> findMin [ ^ self subclassResponsibility ] +{ #category : 'searching' } +CTAVLAbstractNode >> findMinNode [ + + ^ self subclassResponsibility +] + { #category : 'accessing' } CTAVLAbstractNode >> height [ diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index 9a94b2c..7226777 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -46,12 +46,24 @@ CTAVLNilNode >> findMax [ ^ nil ] +{ #category : 'searching' } +CTAVLNilNode >> findMaxNode [ + + ^ self +] + { #category : 'searching' } CTAVLNilNode >> findMin [ ^ nil ] +{ #category : 'searching' } +CTAVLNilNode >> findMinNode [ + + ^ self +] + { #category : 'accessing' } CTAVLNilNode >> height [ diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index 52bf987..b44c0a3 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -74,6 +74,14 @@ CTAVLNode >> findMax [ ifFalse: [ right findMax ] ] +{ #category : 'searching' } +CTAVLNode >> findMaxNode [ + + ^ right isEmpty + ifTrue: [ self ] + ifFalse: [ right findMaxNode ] +] + { #category : 'searching' } CTAVLNode >> findMin [ @@ -82,6 +90,14 @@ CTAVLNode >> findMin [ ifFalse: [ left findMin ] ] +{ #category : 'searching' } +CTAVLNode >> findMinNode [ + + ^ left isEmpty + ifTrue: [ self ] + ifFalse: [ left findMinNode ] +] + { #category : 'accessing' } CTAVLNode >> height [ diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index 6028a52..07f069c 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -46,6 +46,15 @@ CTAVLTree >> addAll: aCollection [ ^ aCollection ] +{ #category : 'enumerating' } +CTAVLTree >> anySatisfy: aBlock [ + + self inOrderDo: [ :each | + (aBlock value: each) ifTrue: [ ^ true ] + ]. + ^ false +] + { #category : 'converting' } CTAVLTree >> asArray [ @@ -55,12 +64,38 @@ CTAVLTree >> asArray [ ^ result asArray ] +{ #category : 'accessing' } +CTAVLTree >> at: anObject ifAbsent: aBlock [ + + | result | + result := root search: anObject. + ^ result ifNil: [ aBlock value ] ifNotNil: [ result ] +] + { #category : 'removing' } CTAVLTree >> clear [ root := CTAVLNilNode new ] +{ #category : 'enumerating' } +CTAVLTree >> collect: aBlock [ + + | result | + result := OrderedCollection new: self size. + self inOrderDo: [ :each | result add: (aBlock value: each) ]. + ^ result +] + +{ #category : 'enumerating' } +CTAVLTree >> detect: aBlock ifNone: absentBlock [ + + self inOrderDo: [ :each | + (aBlock value: each) ifTrue: [ ^ each ] + ]. + ^ absentBlock value +] + { #category : 'enumerating' } CTAVLTree >> do: aBlock [ @@ -76,6 +111,14 @@ CTAVLTree >> findMax [ ifFalse: [ root findMax ] ] +{ #category : 'searching' } +CTAVLTree >> findMaxNode [ + + ^ self isEmpty + ifTrue: [ nil ] + ifFalse: [ root findMaxNode ] +] + { #category : 'searching' } CTAVLTree >> findMin [ @@ -84,6 +127,20 @@ CTAVLTree >> findMin [ ifFalse: [ root findMin ] ] +{ #category : 'searching' } +CTAVLTree >> findMinNode [ + + ^ self isEmpty + ifTrue: [ nil ] + ifFalse: [ root findMinNode ] +] + +{ #category : 'accessing' } +CTAVLTree >> first [ + + ^ self findMin +] + { #category : 'accessing' } CTAVLTree >> height [ @@ -115,6 +172,12 @@ CTAVLTree >> isEmpty [ ^ root isEmpty ] +{ #category : 'accessing' } +CTAVLTree >> last [ + + ^ self findMax +] + { #category : 'enumerating' } CTAVLTree >> postOrderDo: aBlock [ @@ -150,6 +213,15 @@ CTAVLTree >> root [ ^ root isEmpty ifTrue: [ nil ] ifFalse: [ root ] ] +{ #category : 'enumerating' } +CTAVLTree >> select: aBlock [ + + | result | + result := OrderedCollection new. + self inOrderDo: [ :each | (aBlock value: each) ifTrue: [ result add: each ] ]. + ^ result +] + { #category : 'accessing' } CTAVLTree >> size [ ^ root size From e2e737bc32610294cba65ec6db7cdcd2743fd2ce Mon Sep 17 00:00:00 2001 From: Alokzh Date: Sun, 3 Aug 2025 11:11:40 +0530 Subject: [PATCH 07/10] Implemented Range Query operations --- .../CTAVLTreeTest.class.st | 58 +++++++++++++++++++ .../CTAVLAbstractNode.class.st | 31 ++++++++++ src/Containers-AVL-Tree/CTAVLNilNode.class.st | 30 ++++++++++ src/Containers-AVL-Tree/CTAVLNode.class.st | 48 +++++++++++++++ src/Containers-AVL-Tree/CTAVLTree.class.st | 40 +++++++++++++ 5 files changed, 207 insertions(+) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index 9dbf00d..a012feb 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -108,6 +108,49 @@ CTAVLTreeTest >> testDuplicateHandling [ self assert: tree size equals: 2 ] +{ #category : 'tests' } +CTAVLTreeTest >> testElementsFromTo [ + + | result | + tree addAll: #(50 30 70 20 40 60 80). + + result := tree elementsFrom: 35 to: 65. + self assert: result asArray equals: #(40 50 60). + + "Test edge cases" + result := tree elementsFrom: 10 to: 15. + self assert: result isEmpty. + + result := tree elementsFrom: 65 to: 35. "Invalid range" + self assert: result isEmpty +] + +{ #category : 'tests' } +CTAVLTreeTest >> 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 +] + +{ #category : 'tests' } +CTAVLTreeTest >> 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 +] + { #category : 'tests' } CTAVLTreeTest >> testEmpty [ @@ -220,6 +263,21 @@ CTAVLTreeTest >> testPreOrderTraversal [ self assert: result first equals: tree root contents ] +{ #category : 'tests' } +CTAVLTreeTest >> testPredecessorSuccessor [ + + tree addAll: #(50 30 70 20 40 60 80). + + self assert: (tree predecessorOf: 50) equals: 40. + self assert: (tree predecessorOf: 20) isNil. + self assert: (tree successorOf: 50) equals: 60. + self assert: (tree successorOf: 80) isNil. + + "Test with non-existent element" + self assert: (tree predecessorOf: 35) equals: 30. + self assert: (tree successorOf: 35) equals: 40 +] + { #category : 'tests' } CTAVLTreeTest >> testRLRotation [ diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index 81bb861..4de4d9b 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -46,6 +46,25 @@ CTAVLAbstractNode >> contents: anObject [ ^ self subclassResponsibility ] +{ #category : 'enumerating' } +CTAVLAbstractNode >> elementsFrom: min to: max into: aCollection [ + + ^ self subclassResponsibility + +] + +{ #category : 'enumerating' } +CTAVLAbstractNode >> elementsGreaterThan: anObject into: aCollection [ + + ^ self subclassResponsibility +] + +{ #category : 'enumerating' } +CTAVLAbstractNode >> elementsLessThan: anObject into: aCollection [ + + ^ self subclassResponsibility +] + { #category : 'searching' } CTAVLAbstractNode >> findMax [ @@ -118,6 +137,12 @@ CTAVLAbstractNode >> preOrderDo: aBlock [ ^ self subclassResponsibility ] +{ #category : 'searching' } +CTAVLAbstractNode >> predecessorOf: anObject [ + + ^ self subclassResponsibility +] + { #category : 'removing' } CTAVLAbstractNode >> removeValue: anObject [ @@ -136,6 +161,12 @@ CTAVLAbstractNode >> size [ ^ self subclassResponsibility ] +{ #category : 'searching' } +CTAVLAbstractNode >> successorOf: anObject [ + + ^ self subclassResponsibility +] + { #category : 'validation' } CTAVLAbstractNode >> validateAsRoot [ diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index 7226777..9837d6d 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -40,6 +40,24 @@ CTAVLNilNode >> contents: anObject [ "Do nothing for nil node" ] +{ #category : 'enumerating' } +CTAVLNilNode >> elementsFrom: min to: max into: aCollection [ + + "Do Nothing for Nil Node" +] + +{ #category : 'enumerating' } +CTAVLNilNode >> elementsGreaterThan: anObject into: aCollection [ + + "Do Nothing for Nil Node" +] + +{ #category : 'enumerating' } +CTAVLNilNode >> elementsLessThan: anObject into: aCollection [ + + "Do Nothing for Nil Node" +] + { #category : 'searching' } CTAVLNilNode >> findMax [ @@ -99,6 +117,12 @@ CTAVLNilNode >> preOrderDo: aBlock [ "Do Nothing for Nil Node" ] +{ #category : 'searching' } +CTAVLNilNode >> predecessorOf: anObject [ + + ^ nil +] + { #category : 'removing' } CTAVLNilNode >> removeValue: anObject [ @@ -117,6 +141,12 @@ CTAVLNilNode >> size [ ^ 0 ] +{ #category : 'searching' } +CTAVLNilNode >> successorOf: anObject [ + + ^ nil +] + { #category : 'validation' } CTAVLNilNode >> validateAsRoot [ diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index b44c0a3..72211ac 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -66,6 +66,34 @@ CTAVLNode >> contents: anObject [ contents := anObject ] +{ #category : 'enumerating' } +CTAVLNode >> elementsFrom: min to: max into: aCollection [ + + contents > min ifTrue: [ + left elementsFrom: min to: max into: aCollection ]. + (contents between: min and: max) ifTrue: [ aCollection add: contents ]. + contents < max ifTrue: [ + right elementsFrom: min to: max into: aCollection ] +] + +{ #category : 'enumerating' } +CTAVLNode >> elementsGreaterThan: anObject into: aCollection [ + + contents > anObject ifTrue: [ + left elementsGreaterThan: anObject into: aCollection. + aCollection add: contents ]. + right elementsGreaterThan: anObject into: aCollection +] + +{ #category : 'enumerating' } +CTAVLNode >> elementsLessThan: anObject into: aCollection [ + + left elementsLessThan: anObject into: aCollection. + contents < anObject ifFalse: [ ^ self ]. + aCollection add: contents. + right elementsLessThan: anObject into: aCollection +] + { #category : 'searching' } CTAVLNode >> findMax [ @@ -161,6 +189,16 @@ CTAVLNode >> preOrderDo: aBlock [ right preOrderDo: aBlock ] +{ #category : 'searching' } +CTAVLNode >> predecessorOf: anObject [ + + | rightResult | + anObject <= contents ifTrue: [ + ^ left predecessorOf: anObject ]. + rightResult := right predecessorOf: anObject. + ^ rightResult ifNil: [ contents ] ifNotNil: [ rightResult ] +] + { #category : 'private' } CTAVLNode >> rebalance [ @@ -259,6 +297,16 @@ CTAVLNode >> size [ ^ 1 + left size + right size ] +{ #category : 'searching' } +CTAVLNode >> successorOf: anObject [ + + | leftResult | + anObject >= contents ifTrue: [ + ^ right successorOf: anObject ]. + leftResult := left successorOf: anObject. + ^ leftResult ifNil: [ contents ] ifNotNil: [ leftResult ] +] + { #category : 'private' } CTAVLNode >> updateHeight [ diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index 07f069c..905c1d4 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -103,6 +103,34 @@ CTAVLTree >> do: aBlock [ self inOrderDo: aBlock ] +{ #category : 'enumerating' } +CTAVLTree >> elementsFrom: min to: max [ + + | result | + min > max ifTrue: [ ^ #() ]. + result := OrderedCollection new. + root elementsFrom: min to: max into: result. + ^ result +] + +{ #category : 'enumerating' } +CTAVLTree >> elementsGreaterThan: anObject [ + + | result | + result := OrderedCollection new. + root elementsGreaterThan: anObject into: result. + ^ result +] + +{ #category : 'enumerating' } +CTAVLTree >> elementsLessThan: anObject [ + + | result | + result := OrderedCollection new. + root elementsLessThan: anObject into: result. + ^ result +] + { #category : 'searching' } CTAVLTree >> findMax [ @@ -190,6 +218,12 @@ CTAVLTree >> preOrderDo: aBlock [ root preOrderDo: aBlock ] +{ #category : 'searching' } +CTAVLTree >> predecessorOf: anObject [ + + ^ root predecessorOf: anObject +] + { #category : 'removing' } CTAVLTree >> remove: anObject [ @@ -227,6 +261,12 @@ CTAVLTree >> size [ ^ root size ] +{ #category : 'searching' } +CTAVLTree >> successorOf: anObject [ + + ^ root successorOf: anObject +] + { #category : 'accessing' } CTAVLTree >> validate [ From 5e5b9a5e8ef0c5097e6310171bfda93419b5b916 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Sun, 3 Aug 2025 17:50:35 +0530 Subject: [PATCH 08/10] Add remaining utility methods & comprehensive edge case testing --- .../CTAVLTreeTest.class.st | 133 ++++++++++++++++++ src/Containers-AVL-Tree/CTAVLTree.class.st | 32 +++++ 2 files changed, 165 insertions(+) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index a012feb..1a00b00 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -76,6 +76,35 @@ CTAVLTreeTest >> testCollect [ self assert: doubled asArray equals: #(40 60 80 100 140) ] +{ #category : 'tests' } +CTAVLTreeTest >> testComplexRotations [ + + "Test more complex rotation scenarios" + tree addAll: #(50 30 70 20 40 60 80 10). + self assert: tree validate. + + "Add more elements to trigger multiple rotations" + tree addAll: #(5 15 25 35 45). + self assert: tree validate. + + "Height should remain logarithmic" + self assert: tree height <= 5 +] + +{ #category : 'tests' } +CTAVLTreeTest >> 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' } CTAVLTreeTest >> testDetect [ @@ -159,6 +188,16 @@ CTAVLTreeTest >> testEmpty [ self assert: tree height equals: 0 ] +{ #category : 'tests' } +CTAVLTreeTest >> testEmptyTreeOperations [ + + self assert: tree findMin isNil. + self assert: tree findMax isNil. + self deny: (tree includes: 42). + + self deny: (tree anySatisfy: [ :each | true ]) +] + { #category : 'tests' } CTAVLTreeTest >> testFindMinMax [ @@ -199,6 +238,18 @@ CTAVLTreeTest >> testHeight [ self assert: tree height equals: 2 ] +{ #category : 'tests' } +CTAVLTreeTest >> testIfEmptyIfNotEmpty [ + + | result | + result := tree ifEmpty: [ #empty ]. + self assert: result equals: #empty. + + tree add: 42. + result := tree ifNotEmpty: [ :t | #notEmpty ]. + self assert: result equals: #notEmpty +] + { #category : 'tests' } CTAVLTreeTest >> testInOrderTraversal [ @@ -221,6 +272,17 @@ CTAVLTreeTest >> testIncludes [ self deny: (tree includes: 99) ] +{ #category : 'tests' } +CTAVLTreeTest >> testIsLeaf [ + + tree add: 50. + self assert: tree root isLeaf. + + tree add: 30. + self deny: tree root isLeaf. + self assert: tree root left isLeaf +] + { #category : 'tests' } CTAVLTreeTest >> testLLRotation [ @@ -243,6 +305,27 @@ CTAVLTreeTest >> testLRRotation [ self assert: tree asArray equals: #(1 2 3) ] +{ #category : 'tests' } +CTAVLTreeTest >> testNegativeNumbers [ + + tree addAll: #(-10 -5 0 5 10). + + self assert: tree findMin equals: -10. + self assert: tree findMax equals: 10. + self assert: tree asArray equals: #(-10 -5 0 5 10). + self assert: tree validate +] + +{ #category : 'tests' } +CTAVLTreeTest >> testParentChildRelationships [ + + tree addAll: #(50 30 70 20 40). + + self assert: tree root parent isNil. + self assert: tree root left parent equals: tree root. + self assert: tree root right parent equals: tree root +] + { #category : 'tests' } CTAVLTreeTest >> testPostOrderTraversal [ @@ -300,6 +383,31 @@ CTAVLTreeTest >> testRRRotation [ self assert: tree asArray equals: #(1 2 3) ] +{ #category : 'tests' } +CTAVLTreeTest >> testRemovalCausingRebalancing [ + + tree addAll: #(50 30 70 20 40 60 80 10 25). + + "Remove elements that should trigger rebalancing" + tree remove: 10. + tree remove: 20. + tree remove: 25. + + self assert: tree validate +] + +{ #category : 'tests' } +CTAVLTreeTest >> testRemoveAll [ + + tree addAll: #(50 30 70 20 40). + tree removeAll: #(30 70). + + self assert: tree size equals: 3. + self deny: (tree includes: 30). + self deny: (tree includes: 70). + self assert: tree validate +] + { #category : 'tests' } CTAVLTreeTest >> testRemoveLeafNode [ @@ -355,6 +463,18 @@ CTAVLTreeTest >> testRemoveRoot [ self assert: tree validate ] +{ #category : 'tests' } +CTAVLTreeTest >> testReverseInsertions [ + + 15 to: 1 by: -1 do: [ :i | + tree add: i. + self assert: tree validate + ]. + + self assert: tree height <= 5. + self assert: tree asArray equals: (1 to: 15) asArray +] + { #category : 'tests' } CTAVLTreeTest >> testSelect [ @@ -365,6 +485,19 @@ CTAVLTreeTest >> testSelect [ self assert: evens asArray equals: #(20 30 40 50 70) ] +{ #category : 'tests' } +CTAVLTreeTest >> testSequentialInsertions [ + + "Test inserting elements in order (worst case for unbalanced BST)" + 1 to: 15 do: [ :i | + tree add: i. + self assert: tree validate + ]. + + "Height should remain logarithmic" + self assert: tree height <= 5 +] + { #category : 'tests' } CTAVLTreeTest >> testValidation [ diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index 905c1d4..ea2890f 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -87,6 +87,15 @@ CTAVLTree >> collect: aBlock [ ^ result ] +{ #category : 'copying' } +CTAVLTree >> copy [ + + | newTree | + newTree := self class new. + self inOrderDo: [ :each | newTree add: each ]. + ^ newTree +] + { #category : 'enumerating' } CTAVLTree >> detect: aBlock ifNone: absentBlock [ @@ -175,6 +184,22 @@ CTAVLTree >> height [ ^ root height ] +{ #category : 'testing' } +CTAVLTree >> ifEmpty: aBlock [ + + ^ self isEmpty + ifTrue: [ aBlock value ] + ifFalse: [ self ] +] + +{ #category : 'testing' } +CTAVLTree >> ifNotEmpty: aBlock [ + + ^ self isEmpty + ifFalse: [ aBlock value: self ] + ifTrue: [ self ] +] + { #category : 'enumerating' } CTAVLTree >> inOrderDo: aBlock [ @@ -241,6 +266,13 @@ CTAVLTree >> remove: anObject ifAbsent: aBlock [ ^ anObject ] +{ #category : 'removing' } +CTAVLTree >> removeAll: aCollection [ + + aCollection do: [ :each | self remove: each ifAbsent: [ ] ]. + ^ aCollection +] + { #category : 'accessing' } CTAVLTree >> root [ From c06e5ab213183543a8c16da98de79afddf7ab50a Mon Sep 17 00:00:00 2001 From: Alokzh Date: Mon, 4 Aug 2025 12:49:54 +0530 Subject: [PATCH 09/10] Add proper class description --- .../CTAVLTreeTest.class.st | 6 +++- .../CTAVLAbstractNode.class.st | 16 ++++------ src/Containers-AVL-Tree/CTAVLNilNode.class.st | 9 ++---- src/Containers-AVL-Tree/CTAVLNode.class.st | 18 ++--------- src/Containers-AVL-Tree/CTAVLTree.class.st | 30 +++++++------------ 5 files changed, 26 insertions(+), 53 deletions(-) diff --git a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st index 1a00b00..442fe52 100644 --- a/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st +++ b/src/Containers-AVL-Tree-Tests/CTAVLTreeTest.class.st @@ -1,5 +1,9 @@ " -An AVLTest is a test class for testing the behavior of AVL +I test the AVL Tree implementation (CTAVLTree). +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 AVL tree maintains both BST ordering property and AVL balance property through all operations, including automatic rebalancing via rotations. +I test all four rotation cases and verify O(log n) height guarantees. + " Class { #name : 'CTAVLTreeTest', diff --git a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st index 4de4d9b..c75646a 100644 --- a/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLAbstractNode.class.st @@ -1,16 +1,10 @@ " -AVLAbstractNode is an abstract class that represents a node in an AVL tree. - -AVL trees are self-balancing binary search trees. This abstract node class is designed to be subclassed by concrete AVL node classes. It provides common methods and instance variables that are essential for maintaining the balance of the AVL tree. - -Subclasses of AVLAbstractNode should implement methods for adding, removing, and manipulating nodes in the tree to ensure that the tree remains balanced. - +I am an abstract base class for AVL Tree nodes. +I define the common interface that both regular nodes (CTAVLNode) and nil nodes (CTAVLNilNode) must implement. +I include protocol for AVL-specific operations like height, balance factor, and rebalancing, as well as standard tree enumeration and searching. Subclasses: -- AVLNilNode -- AVLNode - -Author: Milton Mamani -Date: October 20, 2023 +- CTAVLNilNode +- CTAVLNode " Class { #name : 'CTAVLAbstractNode', diff --git a/src/Containers-AVL-Tree/CTAVLNilNode.class.st b/src/Containers-AVL-Tree/CTAVLNilNode.class.st index 9837d6d..1ac49a8 100644 --- a/src/Containers-AVL-Tree/CTAVLNilNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNilNode.class.st @@ -1,10 +1,7 @@ " -AVLNilNode is a special sentinel node used in AVL trees to represent the absence of a node. - -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. - -Author: Milton Mamani -Date: October 20, 2023 +I represent an empty node in an AVL 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 CTAVLNode containing the element, effectively growing the tree. I maintain height 0 and am always balanced " Class { #name : 'CTAVLNilNode', diff --git a/src/Containers-AVL-Tree/CTAVLNode.class.st b/src/Containers-AVL-Tree/CTAVLNode.class.st index 72211ac..484f299 100644 --- a/src/Containers-AVL-Tree/CTAVLNode.class.st +++ b/src/Containers-AVL-Tree/CTAVLNode.class.st @@ -1,19 +1,7 @@ " -AVLNode represents a node in an AVL tree. - -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. - -AVLNode instances hold a `contents` instance variable. These nodes are organized to keep the tree balanced, ensuring efficient operations like insertion, deletion, and search. - -This class should not be used directly; instead use `AVLTree`. - -Instance Variables: -- contents: The value associated with this node. -- left: The left child node. -- right: The right child node. - -Author: Milton Mamani -Date: October 20, 2023 +I represent a node in an AVL Tree. +I extend the functionality of a standard BST node by storing my height and performing self-balancing rotations after any modification (add or remove) to ensure the tree's height balance is maintained. +The AVL invariant I maintain is: |height(left) - height(right)| <= 1 " Class { #name : 'CTAVLNode', diff --git a/src/Containers-AVL-Tree/CTAVLTree.class.st b/src/Containers-AVL-Tree/CTAVLTree.class.st index ea2890f..155a8a0 100644 --- a/src/Containers-AVL-Tree/CTAVLTree.class.st +++ b/src/Containers-AVL-Tree/CTAVLTree.class.st @@ -1,25 +1,15 @@ " -AVLTree is an implementation of a self-balancing AVL (Adelson-Velsky and Landis) binary search tree. - -An AVL tree is a binary search tree in which the heights of the two child subtrees of every node differ by at most one. This self-balancing property ensures that the tree remains approximately balanced, leading to efficient insertion, deletion, and search operations. The instances of `AVLTree` uses AVLNode to manage the overall structure of the AVL tree =, - -`AVLTree` is a generic tree that can store comparable objects. It uses `AVLNode` instances to represent the nodes within the tree. - +I represent an AVL Tree, a self-balancing binary search tree. +I maintain the BST property (left < node < right) while also ensuring that the height difference between the left and right subtrees of any node is at most 1. This balancing act guarantees O(log n) worst-case performance for all primary operations. +My public API is designed to be polymorphic with CTBinarySearchTree and includes a rich set of collection methods for enumeration, searching, and accessing. Usage: - To use `AVLTree`, create an instance of the class and then use the provided methods to insert, remove, or search for elements within the tree. The tree will automatically self-balance as elements are added or removed. - -Example: -``` -| tree | -tree := AVLTree new. -tree add: 41. -tree add: 87. -tree add: 20. -tree remove: 87. -tree -``` -Author: Milton -Date: October 20, 2023 + tree := CTAVLTree new. + tree addAll: #(50 30 70 20 40 60 80). + tree asArray. => #(20 30 40 50 60 70 80) + tree height. => 3 + tree first. => 20 + tree includes: 30. => true + tree elementsFrom: 30 to: 60. => #(30 40 50 60) " Class { #name : 'CTAVLTree', From e730eea70403c841e96d6d80c5b6dae7bf262b90 Mon Sep 17 00:00:00 2001 From: Alokzh Date: Mon, 4 Aug 2025 13:01:04 +0530 Subject: [PATCH 10/10] Improved CI configuration and update baseline --- .github/workflows/CI.yml | 27 ++++++++++++ .github/workflows/runTests.yml | 43 ------------------- .smalltalk.ston | 14 ++++++ .../BaselineOfContainersAVLTree.class.st | 28 ++++-------- 4 files changed, 49 insertions(+), 63 deletions(-) create mode 100644 .github/workflows/CI.yml delete mode 100644 .github/workflows/runTests.yml create mode 100644 .smalltalk.ston diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..50fbc4d --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,27 @@ +name: CI +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: +jobs: + build: + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + smalltalk: [Pharo64-13, Pharo64-12, Pharo64-11, Pharo64-10] + runs-on: ${{ matrix.os }} + name: ${{ matrix.smalltalk }} on ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - name: Setup SmalltalkCI + uses: hpi-swa/setup-smalltalkCI@v1 + with: + smalltalk-version: ${{ matrix.smalltalk }} + - name: Load and Test + run: smalltalkci -s ${{ matrix.smalltalk }} + shell: bash + timeout-minutes: 15 \ No newline at end of file diff --git a/.github/workflows/runTests.yml b/.github/workflows/runTests.yml deleted file mode 100644 index eaf157a..0000000 --- a/.github/workflows/runTests.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: CI - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - branches: [ main ] - pull_request: - branches: [ main ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - - - name: Cancel Previous Runs - uses: styfle/cancel-workflow-action@0.12.0 - with: - access_token: ${{ github.token }} - - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - # depth 0 will download all the repository history - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - # Runs tests in a pharo image by removing the old code in the pharo image - - name: Run Pharo Tests - id: tests - uses: akevalion/PharoTestsAction@v1 - with: - pharo: 'pharo12' - baseline: 'ContainersAVLTree' - group: 'default' - tests: 'AVL-Tree' diff --git a/.smalltalk.ston b/.smalltalk.ston new file mode 100644 index 0000000..c388f16 --- /dev/null +++ b/.smalltalk.ston @@ -0,0 +1,14 @@ +SmalltalkCISpec { + #loading : [ + SCIMetacelloLoadSpec { + #baseline : 'ContainersAVLTree', + #directory : 'src', + #platforms : [ #pharo ] + } + ], + #testing : { + #coverage : { + #packages : [ 'Containers-AVL-Tree' ] + } + } +} \ No newline at end of file diff --git a/src/BaselineOfContainersAVLTree/BaselineOfContainersAVLTree.class.st b/src/BaselineOfContainersAVLTree/BaselineOfContainersAVLTree.class.st index bad41bd..d37a9e8 100644 --- a/src/BaselineOfContainersAVLTree/BaselineOfContainersAVLTree.class.st +++ b/src/BaselineOfContainersAVLTree/BaselineOfContainersAVLTree.class.st @@ -1,5 +1,5 @@ " -I am a baseline +I represent Baseline of AVL Tree " Class { #name : 'BaselineOfContainersAVLTree', @@ -10,22 +10,10 @@ Class { { #category : 'baselines' } BaselineOfContainersAVLTree >> baseline: spec [ - - - spec for: #common do: [ "Packages" - spec package: 'Containers-AVL-Tree'. - spec - package: 'Containers-AVL-Tree-Tests' - with: [ spec requires: #( 'Containers-AVL-Tree' ) ]. - spec - package: 'Containers-AVL-Tree-Inspector' - with: [ spec requires: #( 'Containers-AVL-Tree' ) ]. - - "Define groups" - spec - group: 'Core' - with: #( 'Containers-AVL-Tree' 'Containers-AVL-Tree-Inspector' ); - group: 'Tests' with: #( 'Containers-AVL-Tree-Tests' ). - - spec group: 'default' with: #( 'Core' 'Tests' ) ] -] + + spec for: #common do: [ + spec package: 'Containers-AVL-Tree'. + spec + package: 'Containers-AVL-Tree-Tests' + with: [ spec requires: #( 'Containers-AVL-Tree' ) ] ] +] \ No newline at end of file