Find an string in a linked list, two ways one good one BAD!!

Share your advanced PureBasic knowledge/code with the community.
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

Jan Vooijs wrote:Xombie is XSub not a program of yours? Found it somewhere and looks promessing for subtitleing some TV shows I have.

And again if i hit a perfomance barrier i look into yours code too.

Thanks,

Jan V.
It is - I even posted about it in the announcements. I have not updated in a little bit, though ^_^

And you may want to look at the code from sverson instead of mine for a while. I do not have time to test them but I guess my old code is not up to par :) I will have to see if I can remedy that :D
sverson
Enthusiast
Enthusiast
Posts: 286
Joined: Sun Jul 04, 2004 12:15 pm
Location: Germany

Post by sverson »

Jan Vooijs wrote:And until i hit the 10.000.000 (10 million mark i find it in just under 1200ms).
Using 'While NextElement(slTest()) ... Wend' on 10.000.000 elements worst case is 10.000.000 loops.

Using my XTree* on 10.000.000 elements worst case is 24!!! loops which will take below 1ms.
(2^24 = 16.777.216 elements)

*I called it X-Tree because it's not really B-Tree.
[EDIT] BTW it's still beta - maybe i can speed it up a little...

;-) sverson
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

Hmmm... I must be doing something wrong, sverson. I tweaked my old code a little bit and added in your examples to do some testing. With 2,000,000 items, it takes x-tree between 30,000ms to 30,469ms to add the items. My rbtree takes between 4870ms to 5781ms. Lookup on both show 0ms.

I'm attaching the code below to see if I've made some mistake somewhere in the testing. I know I use this computer a lot but I didn't know engineering had progressed so far to make a computer feel emotionally biased toward one programmer's code and not another :)

Code: Select all

;- Notes 
; Borrowed heavily from 'Introduction To Algorithms 2nd Edition' by Cormen, Leiserson, et al. 
;- Constants 
#rbtEmpty = 0 
#rbtRed = 1 
#rbtBlack = 2 
#rbtNull = -1 
#ListSize = 1999999
;- Structures 
Structure s_rbtInfo 
   NodeCount.l 
   ; A count of the nodes in the tree, including the root. 
   AllowDuplicates.b 
   ; Whether or not we allow duplicate keys. 
   UpdateDuplicates.b 
   ; If AllowDuplicates is False, this will control the behavior 
   ; when a duplicate key is passed.  When UpdateDuplicates is True 
   ; we will replace the existing value with the passed value. 
EndStructure 
Structure s_RBT 
   Color.b ; 0 = tree is uninitialized, 1 = red, 2 = black, 3 = Null (a leaf) 
   Parent.l ; A pointer to our parent (if not root) 
   left.l  ; A pointer to our left child 
   right.l ; A pointer to our right child 
   key.l ; Our key (numeric) 
   Value.s ; The value to store. 
EndStructure 
;-
XIncludeFile "X-Tree.pbi"
;-
Global Dim SourceList.s(#ListSize)
Global *XTree.XTree_RN 
;-
Procedure FillSourceList()
  Protected ListPos.l, KeyStr.s, KeyPos.l, EMS.l 
  For ListPos = 0 To #ListSize
    KeyStr="" 
    For KeyPos = 1 To 10 
      KeyStr + RSet(Hex(Random($FF)),2,"0") 
    Next 
    SourceList(ListPos) = KeyStr 
    If ListPos = 298123 : SetClipboardText(KeyStr) : EndIf
  Next 
EndProcedure 
Procedure.s FillXTree() 
  Protected ListPos.l, EMS.l 
  EMS = ElapsedMilliseconds() 
  *XTree.XTree_RN = XTree_NewTree(10) 
  For ListPos = 1 To #ListSize 
    XTree_NewKey(*XTree,SourceList(ListPos)) 
  Next 
  MessageRequester("", "XTree: "+Str(#ListSize)+" Elements stored in " +Str(ElapsedMilliseconds()-EMS)+"ms.")
EndProcedure 
;-
EnableExplicit
;-
; Macro __rbt_AddNode(HoldNode, key, Value, AsLeft = #True)
   ; ;
   ; Define.s_RBT *aNode
   ; ;
   ; *aNode = HoldNode
   ; ;
   ; *NewNode = AllocateMemory(SizeOf(s_RBT))
   ; ;
   ; *NewNode\Parent = *aNode
   ; ;
   ; If key = -1
      ; ;
      ; *NewNode\left = #rbtNull 
      ; *NewNode\right = #rbtNull 
      ; *NewNode\Color = #rbtNull 
      ; ;
   ; Else
      ; ;
      ; *NewNode\Color = #rbtRed 
      ; ;
      ; If AsLeft : *NewNode\left = *aNode\left : Else : *NewNode\right = *aNode\right : EndIf
      ; ; 
      ; *rbtLeaf = AllocateMemory(SizeOf(s_RBT)) 
      ; *rbtLeaf\Color = #rbtNull 
      ; *rbtLeaf\Parent = *NewNode 
      ; *rbtLeaf\right = #rbtNull 
      ; *rbtLeaf\left = #rbtNull 
      ; *rbtLeaf\key = -1 
      ; *rbtLeaf\Value = "" 
      ; ; 
      ; If AsLeft : *NewNode\right = *rbtLeaf : Else : *NewNode\left = *rbtLeaf : EndIf
      ; ;
   ; EndIf
   ; ;
   ; *NewNode\key = key 
   ; *NewNode\Value = Value 
   ; ; 
   ; If AsLeft : *aNode\left = *NewNode : Else : *aNode\right = *NewNode : EndIf
   ; ; 
; EndMacro
Procedure.l __rbt_AddNode(*Node.s_RBT, key.l, Value.s, AsLeft.b = #True)
   ;
   Define.s_RBT *NewNode
   ;
   Define.s_RBT *rbtLeaf
   ;
   *NewNode = AllocateMemory(SizeOf(s_RBT))
   ;
   *NewNode\Parent = *Node
   ;
   If key = -1
      ;
      *NewNode\left = #rbtNull 
      *NewNode\right = #rbtNull 
      *NewNode\Color = #rbtNull 
      ;
   Else
      ;
      *NewNode\Color = #rbtRed 
      ;
      If AsLeft : *NewNode\left = *Node\left : Else : *NewNode\right = *Node\right : EndIf
      ; 
      *rbtLeaf = AllocateMemory(SizeOf(s_RBT)) 
      *rbtLeaf\Color = #rbtNull 
      *rbtLeaf\Parent = *NewNode 
      *rbtLeaf\right = #rbtNull 
      *rbtLeaf\left = #rbtNull 
      *rbtLeaf\key = -1 
      *rbtLeaf\Value = "" 
      ; 
      If AsLeft : *NewNode\right = *rbtLeaf : Else : *NewNode\left = *rbtLeaf : EndIf
      ;
   EndIf
   ;
   *NewNode\key = key 
   *NewNode\Value = Value 
   ; 
   If AsLeft : *Node\left = *NewNode : Else : *Node\right = *NewNode : EndIf
   ;
   ProcedureReturn *NewNode
   ;
EndProcedure
Procedure __rbt_RotateLeft(*Node.s_RBT, *Root.s_RBT) 
   ; 
   Define.l lHold
   Define.s_RBT *Child
   ;
   *Child = *Node\right 
   ; 
   If *Node = *Root 
      ; We rotated our root.  Since we reference the whole tree by the root address, we can't simply attach 
      ; the root to it's child node for the rotation.  This would cause us to falsely reference the old root. 
      lHold = *Node\left 
      *Node\left = *Child\left 
      PokeL(*Child\left + OffsetOf(s_RBT\Parent), *Node)
      *Child\left = lHold 
      PokeL(lHold + OffsetOf(s_RBT\Parent), *Child)
      ; 
      *Node\right = *Child\right 
      PokeL(*Child\right + OffsetOf(s_RBT\Parent), *Node)
      *Child\right = *Node 
      *Child\Parent = *Node\Parent 
      *Node\Parent = *Child 
      ; 
      Swap *Node\key, *Child\key
      Swap *Node\Value, *Child\Value
      Swap *Node\Color, *Child\Color
      ;
      *Node = *Node\Parent 
      ;
      *Child = *Node\right 
      ; 
   EndIf 
   ; 
   lHold = *Child\left 
   *Child\left = *Node 
   *Node\right = lHold 
   ; 
   *Child\Parent = *Node\Parent 
   If *Child <> *Root 
      If PeekL(*Node\Parent + OffsetOf(s_RBT\right)) = *Node 
         PokeL(*Node\Parent + OffsetOf(s_RBT\right), *Child)
      Else 
         PokeL(*Node\Parent + OffsetOf(s_RBT\left), *Child)
      EndIf 
   EndIf 
   *Node\Parent = *Child 
   PokeL(lHold + OffsetOf(s_RBT\Parent), *Node)
   ; 
EndProcedure
Procedure __rbt_RotateRight(*Node.s_RBT, *Root.s_RBT) 
   ; 
   Protected lHold.l 
   Protected *Child.s_RBT 
   ; 
   *Child = *Node\left 
   ; 
   If *Node = *Root 
      ; We rotated our root.  Since we reference the whole tree by the root address, we can't simply attach 
      ; the root to it's child node for the rotation.  This would cause us to falsely reference the old root. 
      lHold = *Node\right 
      *Node\right = *Child\right 
      PokeL(*Child\right + OffsetOf(s_RBT\Parent), *Node)
      *Child\right = lHold 
      PokeL(lHold + OffsetOf(s_RBT\Parent), *Child)
      ; 
      *Node\left = *Child\left 
      PokeL(*Child\left + OffsetOf(s_RBT\Parent), *Node)
      *Child\left = *Node 
      *Child\Parent = *Node\Parent 
      *Node\Parent = *Child 
      ; 
      Swap *Node\key, *Child\key
      Swap *Node\Value, *Child\Value
      Swap *Node\Color, *Child\Color
      ; 
      *Node = *Node\Parent 
      *Child = *Node\left 
      ; 
   EndIf 
   ; 
   lHold = *Child\right 
   *Child\right = *Node 
   *Node\left = lHold 
   ; 
   *Child\Parent = *Node\Parent 
   If *Child <> *Root 
      If PeekL(*Node\Parent + OffsetOf(s_RBT\left)) = *Node 
         PokeL(*Node\Parent + OffsetOf(s_RBT\left), *Child)
      Else 
         PokeL(*Node\Parent + OffsetOf(s_RBT\right), *Child)
      EndIf 
   EndIf 
   *Node\Parent = *Child 
   PokeL(lHold + OffsetOf(s_RBT\Parent), *Node)
   ; 
EndProcedure 
Procedure __rbt_BalanceInsert(*z.s_RBT, *Root.s_RBT) 
   ; 
   Define.s_RBT *pz
   Define.s_RBT *Y
   ; 
   *pz = *z\Parent 
   ; 
   While *pz\Color = #rbtRed 
      ;
      If *pz = PeekL(*pz\Parent + OffsetOf(s_RBT\left))
         ; Our parent is the left child of it's parent. 
         *y = PeekL(*pz\Parent + OffsetOf(s_RBT\right))
         If *y\Color = #rbtRed 
            *pz\Color = #rbtBlack 
            *y\Color = #rbtBlack 
            PokeB(*pz\Parent, #rbtRed)
            *z = *pz\Parent 
         Else 
            If *z = *pz\right 
               *z = *pz 
               __rbt_RotateLeft(*z, *Root) 
               *pz = *z\Parent 
            EndIf 
            *pz\Color = #rbtBlack 
            PokeB(*pz\Parent, #rbtRed) 
            __rbt_RotateRight(*pz\Parent, *Root) 
         EndIf 
      Else 
         ; Our parent is the right child of it's parent. 
         *y = PeekL(*pz\Parent + OffsetOf(s_RBT\left))
         If *y\Color = #rbtRed 
            *pz\Color = #rbtBlack 
            *y\Color = #rbtBlack 
            PokeB(*pz\Parent, #rbtRed) 
            *z = *pz\Parent 
         Else 
            If *z = *pz\left 
               *z = *pz 
               __rbt_RotateRight(*z, *Root) 
               *pz = *z\Parent 
            EndIf 
            *pz\Color = #rbtBlack 
            PokeB(*pz\Parent, #rbtRed) 
            __rbt_RotateLeft(*pz\Parent, *Root) 
         EndIf 
      EndIf 
      *pz = *z\Parent 
   Wend 
   ; 
   *Root\Color = #rbtBlack 
   ; 
EndProcedure 
Procedure.l __rbt_Insert(*Node.s_RBT, key.l, Value.s) ; A private function to insert a key into the tree. 
   ; 
   Define.s_rbtInfo *rbtInfo
   ;
   *rbtInfo = *Node\Parent 
   ; 
   While #True = #True
      ; 
      If key <= *Node\key 
         ; 
         If *Node\key = key 
            ; We found a matching key.  Check to see if we're allowing duplicate keys first. 
            If *rbtInfo\AllowDuplicates = #False 
               ; Guardian routine to see if we're allowing for duplicates. 
               If *rbtInfo\UpdateDuplicates = #True 
                  ; Allowing updates to duplicate keys. 
                  ProcedureReturn *Node 
                  ; We are going to replace the value with the passed value.  Pass the address of the node to be replaced. 
               Else 
                  ProcedureReturn -1 
                  ; We aren't allowing duplicates and we aren't replacing them so don't add this key. 
               EndIf
               ;
            EndIf 
            ;
         EndIf 
         ; If we made it this far then we've either got a duplicate key that we're allowing or we have a non-duplicate key. 
         If PeekB(*Node\left) = #rbtNull 
            ; Left child is a leaf - Null value so add it here. 
            ProcedureReturn __rbt_AddNode(*Node, key, Value, #True)
            ; Return the address of the new node. 
         Else 
            ;
            *Node = *Node\left 
            ; Not a leaf, keep checking by descending the left child. 
         EndIf 
         ; 
      Else
         ;
         If PeekB(*Node\right) = #rbtNull 
            ; Right child is a leaf - Null value so add it here. 
            ProcedureReturn __rbt_AddNode(*Node, key, Value, #False)
            ; Return the address of the new node. 
         Else 
            *Node = *Node\right 
            ; Not a leaf, keep checking by descending the right child. 
         EndIf 
         ;
      EndIf
      ;
   Wend
   ; 
   ProcedureReturn -1 
   ; If we got to this point then we couldn't add it.  Should be because of a Duplicate/Denied addition. 
EndProcedure
Procedure.l __rbt_Successor(*Node.s_RBT) 
   Protected *rbtHold.s_RBT 
   If PeekB(*Node\right) <> #rbtNull 
      ;
      *rbtHold = *Node\right
      ;
      While PeekB(*rbtHold\left) <> #rbtNull : *rbtHold = *rbtHold\left : Wend
      ;
      ProcedureReturn *rbtHold
      ;
   Else 
      *rbtHold = *Node\Parent 
      While *rbtHold\Color <> #rbtEmpty And *Node = *rbtHold\right 
         *Node = *rbtHold 
         *rbtHold = *Node\Parent 
      Wend 
      ; Return the ancestor. 
      ProcedureReturn *rbtHold 
   EndIf 
EndProcedure 
Procedure.l __rbt_Predeccessor(*Node.s_RBT) 
   Protected *rbtHold.s_RBT 
   If PeekB(*Node\left) <> #rbtNull 
      *rbtHold = *Node\left
      While PeekB(*rbtHold\right) <> #rbtNull : *rbtHold = *rbtHold\right : Wend
      ProcedureReturn *rbtHold
   Else 
      *rbtHold = *Node\Parent 
      While *rbtHold\Color <> #rbtEmpty And *Node = *rbtHold\left 
         *Node = *rbtHold 
         *rbtHold = *Node\Parent 
      Wend 
      ; Return the ancestor. 
      ProcedureReturn *rbtHold 
   EndIf 
EndProcedure 
Procedure __rbt_BalanceDelete(*x.s_RBT, *Root.s_RBT) ; A private function to clean up after deleting a black node. 
   ; 
   Protected *px.s_RBT 
   Protected *w.s_RBT 
   ; 
   While *x <> *Root And (*x\Color = #rbtBlack Or *x\Color = #rbtNull) 
      *px = *x\Parent 
      If *px\left = *x 
         ; X is the left child of it's parent. 
         *w = *px\right 
         If *w\Color = #rbtRed 
            *w\Color = #rbtBlack 
            *px\Color = #rbtRed 
            __rbt_RotateLeft(*px, *Root) 
            *w = *px\right 
         EndIf 
         If (PeekB(*w\left) = #rbtBlack Or PeekB(*w\left) = #rbtNull) And (PeekB(*w\right) = #rbtBlack Or PeekB(*w\right) = #rbtNull) 
            *w\Color = #rbtRed 
            *x = *px 
         Else 
            If PeekB(*w\right) = #rbtBlack Or PeekB(*w\right) = #rbtNull 
               PokeB(*w\left, #rbtBlack) ; Check to see if a Null check is required. 
               *w\Color = #rbtRed 
               __rbt_RotateRight(*w, *Root) 
               *w = *px\right
            EndIf 
            *w\Color = *px\Color 
            *px\Color = #rbtBlack 
            PokeB(*w\right, #rbtBlack) ; Check to see if a Null check is required. 
            __rbt_RotateLeft(*px, *Root) 
            *w = *px\right 
            *x = *Root 
         EndIf 
      Else 
         ; X is the right child of it's parent. 
         *w = *px\left 
         If *w\Color = #rbtRed 
            *w\Color = #rbtBlack 
            *px\Color = #rbtRed 
            __rbt_RotateRight(*px, *Root) 
            *w = *px\left 
         EndIf 
         If (PeekB(*w\left) = #rbtBlack Or PeekB(*w\left) = #rbtNull) And (PeekB(*w\right) = #rbtBlack Or PeekB(*w\right) = #rbtNull) 
            *w\Color = #rbtRed 
            *x = *px 
         Else 
            If PeekB(*w\left) = #rbtBlack Or PeekB(*w\left) = #rbtNull 
               PokeB(*w\right, #rbtBlack) ; Check to see if a Null check is required. 
               *w\Color = #rbtRed 
               __rbt_RotateLeft(*w, *Root) 
               *w = *px\left
            EndIf 
            *w\Color = *px\Color 
            *px\Color = #rbtBlack 
            PokeB(*w\left, #rbtBlack) ; Check to see if a Null check is required. 
            __rbt_RotateRight(*px, *Root) 
            *w = *px\left 
            *x = *Root 
         EndIf 
      EndIf 
   Wend 
   ; 
   *x\Color = #rbtBlack 
   ; 
EndProcedure 
Procedure rbt_DestroyDebug(*Node.s_RBT) ; Called to completely destroy our tree. 
   ; 
   Protected lHold.l 
   Protected ChildPath.b 
   Protected *Root.s_RBT 
   Protected *Parent.s_RBT 
   ; 
   ; Debug " " 
   ; Debug " **** Destroying the tree ("+Str(*Node\Key)+")" 
   ; Debug " " 
   *Root = *Node 
   ; Hold our root node address for testing. 
   Repeat 
      ; 
      If *Node = *Root 
         ; We're at the root. 
         ; Debug "At the root ("+Str(*Node\Key)+") with Children ("+Str(rbt_Key(*Node\Left))+", "+Str(*Node\Left)+", "+Str(rbt_Init(*Node\Left))+") And ("+Str(rbt_Key(*Node\Right))+", "+Str(*Node\Right)+", "+Str(rbt_Init(*Node\Right))+")." 
         If PeekB(*Node\left) = #rbtNull 
            ; No left children for the root. 
            If PeekB(*Node\right) = #rbtNull 
               ; Debug "Destroying the root." 
               ; And no right children either.  We're done. 
               Debug "Burning the root's right child ("+Str(PeekL(*Node\right + OffsetOf(s_RBT\key)))+", "+Str(*Node\right)+")." 
               FreeMemory(*Node\right) 
               Debug "Burning the root's left child ("+Str(PeekL(*Node\left + OffsetOf(s_RBT\key)))+", "+Str(*Node\left)+")." 
               FreeMemory(*Node\left) 
               ; Have to free the sentinel nodes 
               Debug "Burning the informational structure." 
               FreeMemory(*Node\Parent) 
               ; Free our informational structure. 
               Debug "Burning the root ("+Str(*Node\key)+", "+Str(*Node)+")." 
               FreeMemory(*Node) 
               *Node = #rbtNull 
               ; We should be totally done - kill the root and exit. 
            Else 
               ; Still a right child left 
               ChildPath = 2 
               *Node = *Node\right 
               *Parent = *Node\Parent 
               ; Debug "Descending right ("+Str(*Node\Key)+", "+Str(*Parent\Key)+")" 
            EndIf 
         Else 
            ; Still a left child left. 
            ChildPath = 1 
            *Node = *Node\left 
            *Parent = *Node\Parent 
            ; Debug "Descending left ("+Str(*Node\Key)+", "+Str(*Parent\Key)+")" 
         EndIf 
      Else 
         ; *Parent = *Node\Parent 
         ; Not at the root. 
         ; Debug "Not at root ("+Str(*Node\Key)+", "+Str(*Parent\Key)+")" 
         If PeekB(*Node\left) = #rbtNull 
            ; No more left children 
            If PeekB(*Node\right) = #rbtNull 
               ; Debug "No left or right child exist ("+Str(*Node\Left)+", "+Str(*Node\Right)+")." 
               ; No more right children or left children.  Free the current element and our procedure should go back to the previous element. 
               If *Parent\left = *Node 
                  ; This was a left child 
                  *Parent\left = *Node\left 
                  ; Copy the Node's left Nil leaf to take the Node's place.  This way our routine will see that the Parent node has a Nil 
                  ; left child and won't traverse that path. 
                  PokeL(*Node\left + OffsetOf(s_RBT\Parent), *Parent) 
                  ; Debug "Moving "+Str(*Node\Left)+" to "+Str(*Parent\Key)+"'s left child." 
                  ; 
                  ChildPath = 1 
                  ; 
                  ; Debug "Burning ("+Str(*Node\Key)+")'s Right child ("+Str(rbt_Key(*Node\Right))+", "+Str(*Node\Right)+")." 
                  FreeMemory(*Node\right) 
                  ; Free up the right leaf (Nil node) since we're moving the left one up to the parent to take Node's place. 
                  If *Node\Color = #rbtRed 
                     ; Debug "Burning ("+Str(*Node\Key)+", "+Str(*Node)+") which is a Left child of ("+Str(*Parent\Key)+") And colored RED." 
                     Debug Str(*Node\key)+",L,"+Str(*Parent\key)+",RED" 
                  Else 
                     ; Debug "Burning ("+Str(*Node\Key)+", "+Str(*Node)+") which is a Left child of ("+Str(*Parent\Key)+") And colored BLACK." 
                     Debug Str(*Node\key)+",L,"+Str(*Parent\key)+",BLACK" 
                  EndIf 
               Else 
                  ; This was a right child 
                  *Parent\right = *Node\right 
                  ; Copy the Node's right Nil leaf to take the Node's place.  This way our routine will see that the Parent node has a Nil 
                  ; right child and won't traverse that path. 
                  PokeL(*Node\right + OffsetOf(s_RBT\Parent), *Parent) 
                  ; Debug "Moving "+Str(*Node\Right)+" to "+Str(*Parent\Key)+"'s right child." 
                  ; 
                  ChildPath = 2 
                  ; Debug "Burning ("+Str(*Node\Key)+")'s left child ("+Str(rbt_Key(*Node\Left))+", "+Str(*Node\Left)+")." 
                  FreeMemory(*Node\left) 
                  ; Free up the left leaf (Nil node) since we're moving the right one up to the parent to take Node's place. 
                  If *Node\Color = #rbtRed 
                     ; Debug "Burning ("+Str(*Node\Key)+", "+Str(*Node)+") which is a Right child of ("+Str(*Parent\Key)+") And colored RED." 
                     Debug Str(*Node\key)+",R,"+Str(*Parent\key)+",RED" 
                  Else 
                     ; Debug "Burning ("+Str(*Node\Key)+", "+Str(*Node)+") which is a Right child of ("+Str(*Parent\Key)+") And colored BLACK." 
                     Debug Str(*Node\key)+",R,"+Str(*Parent\key)+",BLACK" 
                  EndIf 
               EndIf 
               ; 
               ; Kill our leaves for this node. 
               lHold = *Node 
               *Node = *Node\Parent 
               *Parent = *Node\Parent 
               ;lCount + 1 
               FreeMemory(lHold) 
               ; 
            Else 
               ; There are still right children left. 
               ChildPath = 2 
               *Node = *Node\right 
               *Parent = *Node\Parent 
               ; Debug "Descending right ("+Str(*Node\Key)+", "+Str(*Parent\Key)+")" 
            EndIf 
         Else 
            ; Still left children left. 
            ChildPath = 1 
            *Node = *Node\left 
            *Parent = *Node\Parent 
            ; Debug "Descending left ("+Str(*Node\Key)+", "+Str(*Parent\Key)+")" 
         EndIf 
         ; 
      EndIf 
      ; 
   Until *Node = #rbtNull 
   ; 
EndProcedure 
Procedure rbtDestroy(*Node.s_RBT) ; Called to completely destroy our tree. 
   ; 
   Protected lHold.l 
   Protected ChildPath.b 
   Protected *Root.s_RBT 
   Protected *Parent.s_RBT 
   ; 
   *Root = *Node 
   ; Hold our root node address for testing. 
   While *Node <> #rbtNull
      ; 
      If *Node = *Root 
         ; We're at the root. 
         If PeekB(*Node\left) = #rbtNull 
            ; No left children for the root. 
            If PeekB(*Node\right) = #rbtNull 
               ; And no right children either.  We're done. 
               FreeMemory(*Node\right) 
               FreeMemory(*Node\left) 
               ; Have to free the sentinel nodes 
               FreeMemory(*Node\Parent) 
               ; Free our informational structure. 
               FreeMemory(*Node) 
               ;
               *Node = #rbtNull 
               ; We should be totally done - kill the root and exit. 
            Else 
               ; Still a right child left 
               ChildPath = 2 
               *Node = *Node\right 
               *Parent = *Node\Parent 
            EndIf 
         Else 
            ; Still a left child left. 
            ChildPath = 1 
            *Node = *Node\left 
            *Parent = *Node\Parent 
         EndIf 
      Else 
         ; Not at the root. 
         If PeekB(*Node\left) = #rbtNull 
            ; No more left children 
            If PeekB(*Node\right) = #rbtNull 
               ; No more right children or left children.  Free the current element and our procedure should go back to the previous element. 
               If *Parent\left = *Node 
                  ; This was a left child 
                  *Parent\left = *Node\left 
                  ; Copy the Node's left Nil leaf to take the Node's place.  This way our routine will see that the Parent node has a Nil 
                  ; left child and won't traverse that path. 
                  PokeL(*Node\left + OffsetOf(s_RBT\Parent), *Parent)
                  ; 
                  ChildPath = 1 
                  ; 
                  FreeMemory(*Node\right) 
                  ; Free up the right leaf (Nil node) since we're moving the left one up to the parent to take Node's place. 
               Else 
                  ; This was a right child 
                  *Parent\right = *Node\right 
                  ; Copy the Node's right Nil leaf to take the Node's place.  This way our routine will see that the Parent node has a Nil 
                  ; right child and won't traverse that path. 
                  PokeL(*Node\right + OffsetOf(s_RBT\Parent), *Parent)
                  ; 
                  ChildPath = 2 
                  FreeMemory(*Node\left) 
                  ; Free up the left leaf (Nil node) since we're moving the right one up to the parent to take Node's place. 
               EndIf 
               ; 
               ; Kill our leaves for this node. 
               lHold = *Node 
               *Node = *Node\Parent 
               *Parent = *Node\Parent 
               ;lCount + 1 
               FreeMemory(lHold) 
               ; 
            Else 
               ; There are still right children left. 
               ChildPath = 2 
               *Node = *Node\right 
               *Parent = *Node\Parent 
            EndIf 
         Else 
            ; Still left children left. 
            ChildPath = 1 
            *Node = *Node\left 
            *Parent = *Node\Parent 
         EndIf 
         ; 
      EndIf 
      ; 
   Wend
   ; 
EndProcedure 
Procedure rbtRemove(*Root.s_RBT, inKey.l) ; Called to delete a node from our tree. 
   ; 
   Protected *rbtInfo.s_rbtInfo 
   ;
   Define.s_RBT *Node
   ;
   Protected *z.s_RBT 
   Protected *x.s_RBT 
   Protected *y.s_RBT 
   ; 
   *rbtInfo = *Root\Parent 
   ; Our information structure for the tree. 
   *Node = *Root
   ;
   While *Node\Color <> #rbtNull
      If *Node\key = inKey 
         *z = *Node : Break
      Else 
         If inKey <= *Node\key 
            *Node = *Node\left 
         Else 
            *Node = *Node\right 
         EndIf 
      EndIf 
   Wend
   ; Will never return a Nil leaf since we change nodes right before checking this. 
   If *z = 0 : ProcedureReturn : EndIf 
   ; If *z is -1, we didn't find the node to delete.  Exit. 
   If *z = *Root And *rbtInfo\NodeCount = 1 
      ; The root is the only item on the tree and that's what we're deleting. 
      *z\key = -1 
      *z\Value = "" 
      *z\Color = #rbtEmpty 
      *rbtInfo\NodeCount = 0 
      ProcedureReturn 
   EndIf 
   ; 
   If PeekB(*z\left) = #rbtNull Or PeekB(*z\right) = #rbtNull 
      *y = *z 
   Else 
      *y = __rbt_Successor(*z) 
      If *y\Color = #rbtBlack And PeekB(*y\left) = #rbtNull And PeekB(*y\right) = #rbtNull : *y = __rbt_Predeccessor(*z) : EndIf 
   EndIf 
   ; 
   If PeekB(*y\left) <> #rbtNull : *x = *y\left : Else : *x = *y\right : EndIf 
   ; 
   If *y = *Root And *rbtInfo\NodeCount = 2 
      ; There are two nodes on the tree and we're burning the root.  Copy it's non-leaf child into the root and destroy the child. 
      *y\key = *x\key 
      *y\Value = *x\Value 
      If *y\left = *x 
         *y\left = *x\left 
         FreeMemory(*x\right) 
      Else 
         *y\right = *x\right 
         FreeMemory(*x\left) 
      EndIf 
      FreeMemory(*x) 
      ProcedureReturn 
   EndIf 
   ; 
   *x\Parent = *y\Parent 
   ; Set the right child of the node to delete's parent to the node's parent. 
   If *y\Parent <> *rbtInfo 
      If *y = PeekL(*y\Parent + OffsetOf(s_RBT\left)) : PokeL(*y\Parent + OffsetOf(s_RBT\left), *x) : Else : PokeL(*y\Parent + OffsetOf(s_RBT\right), *x) : EndIf 
   EndIf 
   ; If *y\Parent = *rbtInfo 
      ; ; This will be triggered if Y is the root.  It's parent is the information structure. 
      ; ; root[T] = *x 
      ; *Root\Key = *x\Key 
      ; *Root\Value = *x\Value 
      ; ;*Root\Color = *x\Color 
      ; ; I think it's saying that if Y's Parent is NIL (which makes Y the root) then we copy X into the root node, making it the new root instead of Y. 
   ; Else 
      ; If *y = rbt_Left(*y\Parent) : rbt_SetLChild(*y\Parent, *x) : Else : rbt_SetRChild(*y\Parent, *x) : EndIf 
   ; EndIf 
   ; 
   If *y <> *z 
      ; This happens only if the node to delete had two non-leaf children.  If that happened, we searched for a successor to swap with our node-to-delete. 
      *z\key = *y\key 
      *z\Value = *y\Value 
   EndIf 
   ; 
   If *y\Color = #rbtBlack Or *y\Color = #rbtNull 
      FreeMemory(*y) 
      ; Remove our node from memory.  Call this after checking it's color. 
      __rbt_BalanceDelete(*x, *Root) 
      ; Balance our tree after deleting the node. 
   Else 
      ; Red node, no need to call our balancing function. Just remove the node. 
      FreeMemory(*y) 
   EndIf 
   ; 
   *rbtInfo\NodeCount - 1 
   ; Decrement our node count. 
EndProcedure 
Procedure.l rbtMinimum(*NodeAddress.s_RBT) ; Returns the node of the lowest key at the starting node. 
   ; 
   ; Usage: 
   ; 
   ; q.l = rbtCreate() 
   ; rbtAdd(q, 9, "") 
   ; rbtAdd(q, 5, "") 
   ; rbtAdd(q, 4, "") 
   ; rbtAdd(q, 10, "") 
   ; ;   5 
   ; ; 4   9 
   ; ;      10 
   ; Debug rbtMinimum(q) ; Will return '4' 
   ; t.l = rbtIndex(q, 9) 
   ; Debug rbtMinimum(t) ; Will return '9' as there are no smaller keys from that node. 
   While PeekB(*NodeAddress\left) <> #rbtNull : *NodeAddress = *NodeAddress\left : Wend
   ;
   ProcedureReturn *NodeAddress\key 
   ;
EndProcedure 
Procedure.l rbtMaximum(*NodeAddress.s_RBT) ; Returns the node of the highest key at the starting node. 
   ; 
   ; Usage: 
   ; 
   ; q.l = rbtCreate() 
   ; rbtAdd(q, 9, "") 
   ; rbtAdd(q, 5, "") 
   ; rbtAdd(q, 4, "") 
   ; rbtAdd(q, 10, "") 
   ; ;   5 
   ; ; 4   9 
   ; ;      10 
   ; Debug rbtMaximum(q) ; Will return '10' 
   ; t.l = rbtIndex(q, 4) ; Returns the 'index' (address) for the node with a key of '4'. 
   ; Debug rbtMaximum(t) ; Will return '4' as there are no larger keys from that node. 
   While PeekB(*NodeAddress\right) <> #rbtNull : *NodeAddress = *NodeAddress\right : Wend
   ;
   ProcedureReturn *NodeAddress\key 
   ;
EndProcedure 
Procedure.s rbtSeek(*Node.s_RBT, inAddress.l) ; Returns a value based on a node address. 
   *Node = inAddress 
   ProcedureReturn *Node\Value 
EndProcedure 
Procedure.s rbtFind(*Node.s_RBT, inKey.l) ; A private function to return a value based on it's key. 
   While *Node\Color <> #rbtNull
      If *Node\key = inKey 
         ProcedureReturn *Node\Value 
      Else 
         If inKey <= *Node\key 
            *Node = *Node\left 
         Else 
            *Node = *Node\right 
         EndIf 
      EndIf 
   Wend
   ; Will never match against a Nil leaf since we move nodes and then check this condition. 
   ProcedureReturn "" 
EndProcedure 
Procedure.l rbtAdd(*Root.s_RBT, inKey.l, inValue.s) ; Called to add a new key/value pair to our tree. 
   ; 
   Define.s_RBT *Node
   Define.s_rbtInfo *rbtInfo
   ; 
   Define.l lHold
   ; 
   *rbtInfo = *Root\Parent 
   ; 
   If *Root\Color = #rbtEmpty 
      ; If our root node is colored 'Empty', it is uninitialized.  Our new node will go here. 
      *Root\Color = #rbtBlack 
      ; The root is always black. 
      *Root\key = inKey 
      ; Set our key... 
      *Root\Value = inValue 
      ; ...and our value. 
      *rbtInfo\NodeCount = 1 
      ; And now we've inserted the root node so we have 1 node. 
   Else 
      ; Tree is already initialized so add a new element somewhere. 
      lHold = __rbt_Insert(*Root, inKey, inValue) 
      ; Start our insertion routine with the address of the root element. 
      If lHold <> -1 
         ; Return -1 if we did not insert our node. 
         *rbtInfo\NodeCount + 1 
         ; Increase our node count. 
         __rbt_BalanceInsert(lHold, *Root) 
         ; Now balance our newly inserted item if it needs to be balanced. 
         ProcedureReturn lHold 
         ; 
      EndIf 
   EndIf 
   ; 
EndProcedure 
Procedure.l rbtCreate() ; Create the initial tree.  Always call this first and pass it's result to the other functions. 
   ; 
   Define.s_RBT *Tree
   Define.s_rbtInfo *rbtInfo
   ; 
   Define.s_RBT *NewNode
   Define.s_RBT *rbtLeaf
   ;
   *Tree = AllocateMemory(SizeOf(s_RBT)) 
   *Tree\Color = #rbtEmpty 
   ; Our root is always black but we haven't added any nodes yet so 
   ; make it empty first.  When we add a node we'll check this and 
   ; set the new node as the root. 
   *rbtInfo = AllocateMemory(SizeOf(s_rbtInfo)) 
   ; The fake root will not point to a parent node but will instead 
   ; point to our information structure.  We'll use this to access 
   ; optional parameters within the tree. 
   *Tree\Parent = *rbtInfo 
   ; Our root should always point to the informational structure. 
   *rbtInfo\NodeCount = 0 
   ; No nodes yet. 
   *rbtInfo\AllowDuplicates = #False 
   *rbtInfo\UpdateDuplicates = #False 
   ; Set the defaults for our information structure. 
   __rbt_AddNode(*Tree, -1, "", #True)
   ; Add our Nil left leaf. 
   __rbt_AddNode(*Tree, -1, "", #False)
   ; Add our Nil right leaf. 
   ProcedureReturn *Tree 
   ; Return the address of the root node. 
EndProcedure 
;- Code 
; Define.l q, t
; q = rbtCreate() 
; rbtAdd(q, 9, "") 
; rbtAdd(q, 5, "") 
; rbtAdd(q, 4, "") 
; rbtAdd(q, 10, "") 
; ;   5 
; ; 4   9 
; ;      10 
; Debug rbtMaximum(q) ; Will return '4' 
; ; Debug rbtMinimum(t) ; Will return '9' as there are no smaller keys from that node. 
; rbtDestroy(q)
;-
; Define.l q, h
; q.l = rbtCreate() 
; rbtAdd(q, 10, "Sample text 01") 
; rbtAdd(q, 5, "Sample text 02") 
; h.l = rbtAdd(q, 3, "Sample text 03") 
; Debug "Value is: "+rbtFind(q, 5) 
; Debug "Value is: "+rbtSeek(q, h) 
; Debug "Minimum is: "+Str(rbtMinimum(q)) 
; Debug "Maximum is: "+Str(rbtMaximum(q)) 
; rbtRemove(q, 5) 
; rbt_DestroyDebug(q) 
;-
FillSourceList()
; Fill the source list for testing.
Define.l i, t1, t2
;
Delay(500)
; t1 = GetTickCount_() 
FillXTree()
; Test filling sverson's x-tree.
t1 = ElapsedMilliseconds()
i = XTree_FindKey(*XTree, SourceList(298123))
t2 = ElapsedMilliseconds()
MessageRequester("X-Tree Located "+Str(i), StrQ(t2-t1)) 
;
Define.s a
Define.s_RBT *rbtHold
Define.l q, t1, t2, i, j, k
t1 = ElapsedMilliseconds()
q = rbtCreate() 
For i = 0 To #ListSize : rbtAdd(q, i, SourceList(i)) : Next 
t2 = ElapsedMilliseconds()
MessageRequester("", StrQ(t2-t1)) 
; 
t1 = ElapsedMilliseconds()
a = rbtFind(q, 298123) 
t2 = ElapsedMilliseconds()
MessageRequester(a, StrQ(t2-t1)) 
rbtDestroy(q) 
;-
End
Also, my code could be modified to use any kind of key and/or value but I'm used to indexes & string values for database kinds of things. I guess it's my fault that I didn't look too closely at the original post? :oops:
sverson
Enthusiast
Enthusiast
Posts: 286
Joined: Sun Jul 04, 2004 12:15 pm
Location: Germany

Post by sverson »

Image

Download source (still beta) here: Source & EXE
Image
Post Reply