;;$File idl_tree.pro ;; ;; DESCRIPTION: ;; ;; This set of routines is used to build a simple tree data structure ;; using IDL pointers. ;; ;; ;; MODIFICATION HISTORY: ;; March 1994, -KDB Initial coding ;; August 1996, -KDB Updated to use pointers ;; ;;=========================================================================== ;;$ Procedure Tree_New PRO Tree_New, Tree, cmp_func ;; PURPOSE: ;; Initializes an IDL tree structure ;; ;; PARAMETERS: ;; tree - The new tree data structure ;; ;; cmp_proc - The comparison procedure for the tree data ;; Since the data depends on the user, the ;; user must supply a procedure that has the ;; following format: ;; ;; cmp_func,d1,d2, result ;; ;; result = -1 if d1 < d2 ;; 0 if d1 = d2 ;; 1 if d1 > d2 ;; ;; If cmp_func is undefined (i.e., a null string) return: if(strtrim(cmp_func, 2) eq '')then begin Message, "Data Compairson procedure undefined, aborting", /CONTINUE Return ENDif ;; Make sure the tree data structure is defined: TreeType = {treetype, left:ptr_new(), right:ptr_new(), data:ptr_new()} ;; Make a header for the tree, using anonymous structures: tree = { cnt:0l, cmp:cmp_func, pHead:ptr_new()} end ;;========================================================================= FUNCTION Tree_NewNode, Data ;; PARAMETERS: ;; Data - The data value that will be placed in a node ;; ;; Create a new tree node, insert the data and return the pointer: Tmp = {treetype} tmp.data = ptr_new(data) return, ptr_new(tmp) END ;--------------------------------------------------------------------------- pro Tree_insert, Tree, Data ;; See if Data is defined, Else return an error: if(N_Elements(Data) eq 0)then BEGIN message, "Data value undefined",/continue return ENDif ; Do we have any nodes yet? if(Tree.pHead eq ptr_new())then $ tree.pHead = Tree_NewNode(Data) $ else $ _Tree_Insert, Tree, Data, Tree.pHead end ;;========================================================================== PRO _Tree_Insert, Tree, Data, pNode ;; PURPOSE: ;; This procedure is used to add a new node to the tree specified by ;; the structure Tree. The procedure will recurses on itself until ;; the correct insert location is found on the tree, then it ;; inserts the node and returns. ;; ;; PARAMETERS: ;; Tree - A tree struct ;; ;; Data - The data to be put in the tree ;; ;; pNode - Pointer to the current node. If this is null, insert ;; node here. ;; ;; Use the comparison function for the data to see what do to: Call_Procedure, Tree.cmp, Data, *(*pNode).data, result if( result(0) le 0)then BEGIN if(ptr_valid((*pNode).left))then $ ; continue traverse _Tree_Insert, Tree, Data, (*pNode).left $ else begin (*pNode).left = Tree_NewNode(Data) tree.cnt = tree.cnt+1; endelse ENDif else BEGIN if(ptr_valid((*pNode).right)) then $ _Tree_Insert, Tree, Data, (*pNode).right $ else begin (*pNode).right = Tree_NewNode(Data) tree.cnt = tree.cnt+1; endelse ENDelse END ;;============================================================================ FUNCTION _Tree_Search, Tree, Data, pNode ;; PURPOSE: ;; Searches the tree for a match and returns a null ptr if there is no ;; match and a pointer if there is a match. This function is recursive. ;; ;; PARAMETERS: ;; Tree - The Tree to search ;; ;; Data - The data to search for ;; ;; pNode - Pointer to node forward_function _tree_search if(N_Elements(Data) eq 0) then BEGIN Message, "Data value undefined", /CONTINUE Return, ptr_new() ENDif if(not PTR_VALID(pNode)) then $ Return, ptr_new() ; See if we have a match Call_Procedure, Tree.cmp, Data, *(*pNode).data, cmp_res if(cmp_res lt 0)then $ return, _Tree_Search(Tree, Data, (*pNode).left) $ else if(cmp_res gt 0)then $ return, _Tree_Search(Tree, Data, (*pNode).right) $ else $ return, (*pNode).data END ;--------------------------------------------------------------------------- function Tree_Search, Tree, Data return, _Tree_Search(Tree, Data, Tree.pHead) end ;--------------------------------------------------------------------------- pro Tree_DeleteNode, Tree, Data _Tree_DeleteNode, Tree, Data, Tree.pHead end ;;============================================================================ PRO _Tree_DeleteNode, Tree, Data, pNode ;; PURPOSE: ;; Deletes the first node if finds that contains Data. This routine ;; recurses. ;; ;; PARAMETERS: ;; Tree - The tree you are Deleting from (structure) ;; ;; Data - The data that is to be deleted (structure) ;; ;; pNode - pointer to node if(N_Elements(Data) eq 0)then BEGIN Message, "Data value undefined", /CONTINUE Return ENDif if (not PTR_VALID(pNode) ) then BEGIN Message, "Data not found", /CONTINUE Return ENDif ;; Do we match Call_Procedure, Tree.cmp, Data, *(*pNode).data, cmp_res if(cmp_res lt 0)then $ _Tree_DeleteNode, Tree, Data, (*pNode).left $ else if(cmp_res gt 0)then $ _Tree_DeleteNode, Tree, Data, (*pNode).right $ else BEGIN ; we have a match l_valid = PTR_VALID((*pNode).left) r_valid = PTR_VALID((*pNode).right) if(not l_valid and not r_valid )then begin ; No children, delete the node ptr_free, (*pNode).data ptr_free, pNode endif else if(l_valid and r_valid)then begin ;; Both children are *valid* so we need to find the next smallest ;; child. This is the child that is the farthest left branch of the ;; current right child: pParent = pNode pCurrent = (*pNode).right ;; Go down the left side of the right branch until there are no more ;; valid pointers: While( PTR_VALID((*pCurrent).left))do BEGIN pParent = pCurrent pCurrent = (*pParent).left ENDwhile ;; Replace the current node's data with data from the node ;; we are *splicing* in: ptr_free, (*pNode).data (*pNode).data = (*pCurrent).data if(pParent eq pNode)then $ (*pNode).right = (*pCurrent).right $ else $ (*pParent).left = (*pCurrent).right ptr_free, pCurrent endif else BEGIN ;; Only have one child. Clean up node and move up the child to ;; pNode if(l_valid)then $ pKill = (*pNode).left $ else $ pKill= (*pNode).right ptr_free, (*pNode).data *pNode = *pKill ptr_free, pKill ENDelse ENDelse END ;--------------------------------------------------------------------------- pro Tree_Traverse, Tree, Proc, INORDER=INORDER, $ PREORDER=PREORDER, POSTORDER=POSTORDER _Tree_Traverse, Proc, Tree.pHead, INORDER=INORDER, $ PREORDER=PREORDER, POSTORDER=POSTORDER end ;;===================================================================== PRO _Tree_Traverse, Proc, pNode , INORDER=INORDER, $ PREORDER=PREORDER, POSTORDER=POSTORDER ;; PURPOSE: ;; This function recursivly traverses the tree in the selected order ;; applying the given procedure to each node. ;; ;; PARAMETERS: ;; Proc - Name of the procedure to apply to each node ;; ;; pNode - pointer to current node ;; ;; KEYWORDS: ;; INORDER - Do an inorder traversal ;; ;; PREORDER - Do a preorder traversal ;; ;; POSTORDER - Do a postorder traversal if(not PTR_VALID(pNode))then $ Return if(Keyword_Set(PREORDER))then begin Call_Procedure, Proc, *(*pNode).data _Tree_Traverse, Proc, (*pNode).left , /PREORDER _Tree_Traverse, Proc, (*pNode).right, /PREORDER endif else if( Keyword_Set(POSTORDER))then begin _Tree_Traverse, Proc, (*pNode).left, /POSTORDER _Tree_Traverse, Proc, (*pNode).right, /POSTORDER Call_Procedure, Proc, *(*pNode).data endif else begin _Tree_Traverse, Proc, (*pNode).left , /INORDER Call_Procedure, Proc, *(*pNode).data _Tree_Traverse, Proc, (*pNode).right, /INORDER endelse end ;--------------------------------------------------------------------------- pro Tree_Delete, Tree _Tree_Delete, Tree.pHead end ;;============================================================================ PRO _Tree_Delete, pNode ;; PURPOSE: ;; This procedure is used to delete all of the nodes in the tree. ;; This is just a postorder traversal of the tree. This is done ;; Recursivly. ;; ;; PARAMETER: ;; pNode - The current node. if(not PTR_VALID(pNode))then $ Return _Tree_Delete, (*pNode).left _Tree_Delete, (*pNode).right ptr_free, (*pNode).data ptr_free, pNode end