BinaryTree.st
author Claus Gittinger <cg@exept.de>
Wed, 10 Dec 2003 10:54:24 +0100
changeset 1380 60a7125ce957
parent 1379 ff2b64379b66
child 1475 37c5fb7333f7
permissions -rw-r--r--
*** empty log message ***

"{ Package: 'stx:libbasic2' }"

Collection subclass:#BinaryTree
	instanceVariableNames:'treeRoot sortBlock'
	classVariableNames:''
	poolDictionaries:''
	category:'Collections-Ordered'
!

Object subclass:#BinaryTreeNode
	instanceVariableNames:'data leftSubtree rightSubtree'
	classVariableNames:''
	poolDictionaries:''
	privateIn:BinaryTree
!

!BinaryTree class methodsFor:'documentation'!

documentation
"
    Loosely based on the Public Domain BinaryTreeNode class from Steve Chepurny.

    Changes:
        Changed to be Collection-protocol compatible.
        Slight speedup in insert-code.

    [author:]
        Steve Chepurny (original BinaryTreeNode implementation)
        Claus Gittinger (cg@alan)

    [instance variables:]

    [class variables:]

    [see also:]

"
!

examples
"
                                                                [exBegin]
    |coll|

    coll := BinaryTree new.
    (1 to:10) do:[:i | coll add:i]..
    coll addAll:(20 to:30).
    coll    
                                                                [exEnd]

  timing:  
                                                                [exBegin]
    |N randomNumbers coll1 coll2 t1 t2|

    N := 1000000.
    randomNumbers := (1 to:N) collect:[:i | Random nextInteger].

    ObjectMemory garbageCollect.
    t1 := Time millisecondsToRun:[
        coll1 := BinaryTree new.
        coll1 addAll:randomNumbers
    ].

    coll1 := nil.
    ObjectMemory garbageCollect.
    t2 := Time millisecondsToRun:[
        coll2 := SortedCollection new.
        coll2 addAll:randomNumbers
    ].
    Transcript show:'Time to insert '; show:N; show:' into BinaryTree: '; show:t1; showCR:'ms'.
    Transcript show:'Time to insert '; show:N; show:' into SortedCollection: '; show:t2; showCR:'ms'.
                                                                [exEnd]

  timing 2:  
                                                                [exBegin]
    |allSelectors coll1 coll2 t0 t1 t2|

    allSelectors := OrderedCollection new.
    Smalltalk allClassesDo:[:cls |
        cls instAndClassSelectorsAndMethodsDo:[:sel :mthd |
            allSelectors add:sel.
        ].
    ].

    t1 := Time millisecondsToRun:[
        coll1 := SortedCollection new.
        allSelectors do:[:sel |
            coll1 add:sel
        ].
    ].
    Transcript show:'Time to insert '; show:coll1 size; show:' selectors into SortedCollection: '; show:t1; showCR:'ms'.

    t2 := Time millisecondsToRun:[
        coll2 := BinaryTree new.
        allSelectors do:[:sel |
            coll2 add:sel
        ].
    ].
    Transcript show:'Time to insert '; show:coll2 size; show:' selectors into BinaryTree: '; show:t2; showCR:'ms'.

    t1 := Time millisecondsToRun:[
        allSelectors do:[:sel |
            coll1 remove:sel
        ].
    ].
    self assert:(coll1 isEmpty).
    Transcript show:'Time to remove selectors from SortedCollection: '; show:t1; showCR:'ms'.

    t2 := Time millisecondsToRun:[
        allSelectors do:[:sel |
            coll2 remove:sel
        ].
    ].
    self assert:(coll2 isEmpty).
    Transcript show:'Time to remove selectors from BinaryTree: '; show:t2; showCR:'ms'.
                                                                [exEnd]

"
! !

!BinaryTree class methodsFor:'instance creation'!

new
    ^ self basicNew initialize
!

new:n
    ^ self new
! !

!BinaryTree methodsFor:'adding & removing'!

add:anObject
    |newNode|

    newNode := BinaryTreeNode data:anObject.
    treeRoot isNil ifTrue:[
        treeRoot := newNode.
    ] ifFalse:[
        treeRoot insert:newNode sortBlock:sortBlock
    ].
    ^ anObject "sigh - collection protocol"

    "
     BinaryTree new add:1; add:2; yourself
     BinaryTree with:1 with:2 with:3
    "
!

do:aBlock
    treeRoot notNil ifTrue:[
        treeRoot inOrderDo:[:eachNode | aBlock value:(eachNode data)].
    ].

    "
     BinaryTree new add:1; add:2; yourself
    "
!

includesIdentical:anElement
    treeRoot isNil ifTrue:[
        ^ false.
    ].
    ^ treeRoot includesIdenticalValue:anElement sortBlock:sortBlock.

    "
     BinaryTree new 
        addAll:#(4 2 1 3 6 5 7); 
        includesIdentical:4
    "

    "
     BinaryTree new 
        addAll:#(4 2 1 3 6 5 7); 
        includesIdentical:8
    "
!

remove:oldObject ifAbsent:exceptionValue
    |newRoot|

    treeRoot isNil ifTrue:[
        ^ exceptionValue value.
    ].
    newRoot := treeRoot removeValue:oldObject sortBlock:sortBlock.
    newRoot isNil ifTrue:[
        treeRoot data = oldObject ifFalse:[
            ^ exceptionValue value.
        ].
    ].
    treeRoot := newRoot    

    "
     BinaryTree new 
        addAll:#(4 2 1 3 6 5 7); 
        removeIdentical:4;
        yourself   
    "

    "
     BinaryTree new 
        addAll:#(4 2 1 3 6 5 7); 
        removeIdentical:7;
        yourself      
    "
!

removeIdentical:oldObject ifAbsent:exceptionValue
    |newRoot|

    treeRoot isNil ifTrue:[
        ^ exceptionValue value.
    ].
    newRoot := treeRoot removeIdenticalValue:oldObject sortBlock:sortBlock.
    newRoot isNil ifTrue:[
        ^ exceptionValue value.
    ].
    treeRoot := newRoot    

    "
     BinaryTree new 
        addAll:#(4 2 1 3 6 5 7); 
        removeIdentical:4;
        yourself   
    "

    "
     BinaryTree new 
        addAll:#(4 2 1 3 6 5 7); 
        removeIdentical:7;
        yourself      
    "
! !

!BinaryTree methodsFor:'initialization'!

initialize
    sortBlock := [:a :b | a < b]
! !

!BinaryTree methodsFor:'queries'!

size
    ^ treeRoot size
! !

!BinaryTree::BinaryTreeNode class methodsFor:'documentation'!

copyright
"
    Public domain (I guess, as it was published in c.l.s)
    no limitation on use.

    This class is provided as-is, without any warranty. 
    It is not part of or covered by the ST/X copyright.
"
!

documentation
"
    goody from comp.lang.smalltalk;
    original header:

	Here's a complete implementation of a binary tree class:


    claus:
	I would have called this a BinaryTreeNode ....


    [organization:]
	The National Capital FreeNet, Ottawa, Ontario, Canada

    [author:]
	al938@FreeNet.Carleton.CA (Steve Chepurny)

    [see also:]
	LinkedList Chain
	Link ValueLink ChainLink
"
!

examples
"
  manual building of a tree:
                                                                        [exBegin]
    |tree|

    tree := BinaryTreeNode data:2.
    tree leftSubtree:(BinaryTreeNode new data:1).
    tree rightSubtree:(BinaryTreeNode new data:3).
    tree printOn:Transcript.
                                                                        [exEnd]

  insertion:
                                                                        [exBegin]
    |tree|

    tree := BinaryTreeNode data:'hello'.
    #('the' 'quick' 'brown' 'fox' 'jumps' 'over' 'the' 'lazy' 'dogs')
    do:[:word |
        tree insert:(BinaryTreeNode data:word).
    ].
    tree inOrderDo:[:node |
        Transcript showCR:node data
    ]
                                                                        [exEnd]
"
! !

!BinaryTree::BinaryTreeNode class methodsFor:'instance creation'!

data:data
    "Returns a new binary tree node, holding data"

    ^ self basicNew initialize data:data

    "Modified: 10.5.1996 / 15:00:13 / cg"
    "Created: 10.5.1996 / 15:00:35 / cg"
!

empty
    "Returns a new binary tree with subtrees as binary tree nodes"

    ^ self new
	    leftSubtree: self new;
	    rightSubtree: self new

    "Modified: 10.5.1996 / 15:00:02 / cg"
!

new
    "Returns a new empty binary tree node"

    ^ self basicNew initialize

    "Modified: 10.5.1996 / 15:00:13 / cg"
! !

!BinaryTree::BinaryTreeNode methodsFor:'accessing'!

data
    ^data
!

data:   anObject
    data := anObject
!

leftSubtree
    ^leftSubtree
!

leftSubtree: aBinaryTree
    leftSubtree := aBinaryTree
!

rightSubtree
    ^rightSubtree
!

rightSubtree: aBinaryTree
    rightSubtree := aBinaryTree
! !

!BinaryTree::BinaryTreeNode methodsFor:'enumeration'!

do: aBlock
    "applies aBlock to each elements data in the binary tree in inorder"

    self inOrderDo:[:eachNode | aBlock value:eachNode data]
!

inOrderDo:aBlock
    "Traverses the elements of the binary tree in
        LEFT - ROOT - RIGHT order, 
     applying a block to each node"

    leftSubtree notNil ifTrue:[
        leftSubtree inOrderDo: aBlock
    ].

    aBlock value:self.

    rightSubtree notNil ifTrue:[
        rightSubtree inOrderDo: aBlock
    ].

    "Modified: 10.5.1996 / 15:10:34 / cg"
!

postOrderDo: aBlock
    "Traverses the elements of the binary tree in
        LEFT - RIGHT - ROOT order, 
     applying a block to each node"

    leftSubtree notNil ifTrue:[
        leftSubtree postOrderDo: aBlock
    ].
    rightSubtree notNil ifTrue:[
        rightSubtree postOrderDo: aBlock
    ].

    aBlock value: self.
!

preOrderDo: aBlock
    "Traverses the elements of the binary tree in
        ROOT - LEFT - RIGHT order, 
     applying a block to each node"

    aBlock value: self.

    leftSubtree notNil ifTrue:[
        leftSubtree preOrderDo: aBlock
    ].
    rightSubtree notNil ifTrue:[
        rightSubtree preOrderDo: aBlock
    ].
! !

!BinaryTree::BinaryTreeNode methodsFor:'insert & delete'!

insert:aBinaryTreeNode
    "insert a node, comparing nodes using a default sort rule"

    ^ self
        insert:aBinaryTreeNode
        sortBlock:[:a :b | a < b]

    "Modified: 10.5.1996 / 15:08:30 / cg"
    "Created: 10.5.1996 / 15:09:44 / cg"
!

insert:newBinaryTreeNode sortBlock:sortBlock
    "insert a node, comparing nodes using sortBlock"

"/    "/ the following might be ugly - however, it it slightly faster
"/    "/ than the stuff below.
"/    "/ (we MUST have LCO in smalltalk for this to be automatically fast)
"/
"/    |node newValue left right|
"/
"/    node := self.
"/    newValue := newBinaryTreeNode data.
"/    [true] whileTrue:[
"/        (sortBlock value:newValue value:node data) ifTrue:[
"/            left := node leftSubtree.
"/            left isNil ifTrue:[
"/                node leftSubtree:newBinaryTreeNode.
"/                ^ self
"/            ].
"/            node := left
"/        ] ifFalse:[
"/            right := node rightSubtree.
"/            right isNil ifTrue:[
"/                node rightSubtree:newBinaryTreeNode.
"/                ^ self
"/            ].
"/            node := right
"/        ]
"/    ].
"/    "not reached"

    (sortBlock value:newBinaryTreeNode data value:data) ifTrue:[
        leftSubtree isNil ifTrue:[
            leftSubtree := newBinaryTreeNode.
        ] ifFalse:[
            leftSubtree insert:newBinaryTreeNode sortBlock:sortBlock
        ]
    ] ifFalse:[
        rightSubtree isNil ifTrue:[
            rightSubtree := newBinaryTreeNode.
        ] ifFalse:[
            rightSubtree insert:newBinaryTreeNode sortBlock:sortBlock
        ]
    ]
!

removeIdenticalValue:oldValue sortBlock:sortBlock
    "remove a value - returns a new treeNode, or nil if the value is not in the tree"

    |newTop|

    data == oldValue ifTrue:[
        leftSubtree isNil ifTrue:[
            ^ rightSubtree
        ].
        rightSubtree isNil ifTrue:[
            ^ leftSubtree
        ].
        newTop := self removeLeftRightMostNode.
        newTop leftSubtree:leftSubtree.
        newTop rightSubtree:rightSubtree.
        ^ newTop.
    ].

    (sortBlock value:oldValue value:data) ifTrue:[
        leftSubtree isNil ifTrue:[
            ^ nil
        ].
        leftSubtree := leftSubtree removeIdenticalValue:oldValue sortBlock:sortBlock.
        ^ self. 
    ].
    rightSubtree isNil ifTrue:[
        ^ nil
    ].
    rightSubtree := rightSubtree removeIdenticalValue:oldValue sortBlock:sortBlock.
    ^ self. 
!

removeValue:oldValue sortBlock:sortBlock
    "remove a value - returns a new treeNode, or nil if the value is not in the tree"

    |newTop|

    data = oldValue ifTrue:[
        leftSubtree isNil ifTrue:[
            ^ rightSubtree
        ].
        rightSubtree isNil ifTrue:[
            ^ leftSubtree
        ].
        newTop := self removeLeftRightMostNode.
        newTop leftSubtree:leftSubtree.
        newTop rightSubtree:rightSubtree.
        ^ newTop.
    ].

    (sortBlock value:oldValue value:data) ifTrue:[
        leftSubtree isNil ifTrue:[
            ^ nil
        ].
        leftSubtree := leftSubtree removeValue:oldValue sortBlock:sortBlock.
        ^ self. 
    ].
    rightSubtree isNil ifTrue:[
        ^ nil
    ].
    rightSubtree := rightSubtree removeValue:oldValue sortBlock:sortBlock.
    ^ self. 
! !

!BinaryTree::BinaryTreeNode methodsFor:'printing'!

printOn: aStream
    "Append the ascii representation to aStream"

    data isNil
        ifTrue: [aStream nextPutAll: '--']
        ifFalse: [
            aStream nextPut: $(.
            data printOn: aStream. aStream nextPut: $ .
            leftSubtree printOn: aStream. aStream nextPut: $ .
            rightSubtree printOn: aStream.
            aStream nextPut: $)]
! !

!BinaryTree::BinaryTreeNode methodsFor:'private helpers'!

removeLeftRightMostNode
    |rightMost|

    leftSubtree rightSubtree isNil ifTrue:[
        rightMost := leftSubtree.
        leftSubtree := leftSubtree leftSubtree.
        ^ rightMost.
    ].

    ^ leftSubtree removeRightMostNode

    "
     |tree|

     tree := BinaryTreeNode data:4.
     #(2 6 1 3 5 7)
     do:[:word |
         tree insert:(BinaryTreeNode data:word).
     ].
     Transcript showCR:tree.
     tree removeLeftRightMostNode.
    "
!

removeRightMostNode
    |removedNode|

    rightSubtree isNil ifTrue:[
        self error:'should not happen'
    ].
    rightSubtree rightSubtree notNil ifTrue:[
        ^ rightSubtree removeRightMostNode.
    ].
    removedNode := rightSubtree.
    rightSubtree := nil.
    ^ removedNode

    "
     |tree|

     tree := BinaryTreeNode data:4.
     #(2 6 1 3 5 7)
     do:[:word |
         tree insert:(BinaryTreeNode data:word).
     ].
     Transcript showCR:tree.
     Transcript showCR:(tree removeLeftRightMostNode). 
     Transcript showCR:tree.
    "
! !

!BinaryTree::BinaryTreeNode methodsFor:'queries'!

depth
    "Returns the depth of the binary list"

    ^ self level - 1.
!

getTreeWithAnInteger: anInteger
    "Private - Returns the BinaryTree with data anInteger.  
     If anInteger not in the tree it returns nil."

    self inOrderDo: [:each| each data = anInteger ifTrue:[^each]].
    ^nil.
!

inOrderSuccessor
    "Returns the in-order successor the of receiver.
     (that is the leftMost node on the right side)
     If receiver is empty then returns the receiver."

    rightSubtree isNil ifTrue:[^ self].
    ^ rightSubtree leftMostNode
!

includesIdenticalValue:aValue sortBlock:sortBlock
    "return true, if aValue is contained as some node's data"

    data == aValue ifTrue:[ ^ true ].
    (sortBlock value:aValue value:data) ifTrue:[
        leftSubtree isNil ifTrue:[
            ^ false
        ].
        ^ leftSubtree includesIdenticalValue:aValue sortBlock:sortBlock.
    ].
    rightSubtree isNil ifTrue:[
        ^ false
    ].
    ^ rightSubtree includesIdenticalValue:aValue sortBlock:sortBlock.
!

includesValue:aValue sortBlock:sortBlock
    "return true, if some node's data is equal to aValue"

    data = aValue ifTrue:[ ^ true ].

    (sortBlock value:aValue value:data) ifTrue:[
        leftSubtree isNil ifTrue:[
            ^ false
        ].
        ^ leftSubtree includesIdenticalValue:aValue.
    ].
    rightSubtree isNil ifTrue:[
        ^ false
    ].
    ^ rightSubtree includesIdenticalValue:aValue.
!

isEmpty
    "returns true if the binary tree is empty and false otherwise"

    ^ data isNil
!

isLeaf
    "Returns true if self is a leaf"

    ^ ((leftSubtree isNil) and: [rightSubtree isNil])
!

leftMostNode
    "Returns the leftMost (smallest-valued) node"

    leftSubtree isNil ifTrue:[^ self].
    ^ leftSubtree leftMostNode
!

level
    "Returns the depth of the binary tree"

    |l|

    l := 0.
    leftSubtree notNil ifTrue:[
        l := leftSubtree level
    ].
    rightSubtree notNil ifTrue:[
        l := l max:(rightSubtree level)
    ].
    ^ l + 1
!

rightMostNode
    "Returns the rightMost (largest-valued) node"

    rightSubtree isNil ifTrue:[^ self].
    ^ rightSubtree rightMostNode
!

size
    "Returns the size of the binary tree by traversing each element inorder"

    |count|

    count := 0.
    self inOrderDo: [:each | count := count + 1].
    ^ count
! !

!BinaryTree class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic2/BinaryTree.st,v 1.2 2003-12-10 09:54:24 cg Exp $'
! !