//#pragma title( "TNode.cpp - List/Tree base classes" ) /* Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved. =============================================================================== Module - TNode.cpp System - Common Author - Tom Bernhardt Created - 1989-11-19 Description - List/Tree base classes. TNode is a base class to define a collection element. It contains a left and right pointer to another TNode item and these may be organized as a double-linked linear list or binary tree in the collection classes that use TNode items. Central to its utility are member functions to convert between binary tree, sorted 2-way linear linked lists, and unsorted 2-way linked linear lists. Collection and enum classes TNodeList A simple collection of TNode elements. TNodeListSortable A TNodeList that is sortable by one or more compare functions. Conversion member functions for TNodeListSortable: The form of the list may be easily changed from binary tree to sorted list or vice versa. The following member functions support these transformations: ToSorted Converts the tree form into a sorted linear list form without need for comparisons; the order is preserved. SortedToTree Converts the sorted linear list form into a perfectly balanced binary tree without comparisons; the order is preserved. UnsortedToTree Converts the sorted linear list form into a binary tree that is not necesarily balanced. It uses the PCompare function to form the order of the tree. Thus if the list order closely matches the PCompare directed order, the resulting tree will be grossly unbalanced. This has a bearing on the performance and memory requirements of the ToSorted function which is recursive. So be careful, especially with large lists. Sort This resorts either a tree or list form according to the argument pCompare function pointer provided. Note the above admonition. In either form, exposed are also Insert and Remove member functions. The functions are wrappers for TreeInsert and SortedInsert function depending upon the current list type. Updates - 1995-05-01 TPB Converted to C++ classes. =============================================================================== */ #ifdef USE_STDAFX # include "stdafx.h" #else # include #endif #include #include #include #include "TNode.hpp" #include "common.hpp" #pragma page() //------------------------------------------------------------------------------ // Warning: Must not pass top == NULL //------------------------------------------------------------------------------ TNode * // ret-head of sorted list TNodeListSortable::TreeToSortedList( TNode * top ,// i/o-top of [sub]tree to squash TNode ** newhead ,// out-leftmost branch from tree TNode ** newtail // out-rightmost branch from tree ) { TNode * temp; // temporary pointer placeholder if ( top->left == NULL ) *newhead = top; // this is leftmost of parent node else { TreeToSortedList(top->left, newhead, &temp); top->left = temp; // left = tail of sub-list top->left->right = top; } if ( top->right == NULL ) *newtail = top; // tree is rightmost of parent node else { TreeToSortedList(top->right, &temp, newtail); top->right = temp; // right = head of sub-list top->right->left = top; } return *newhead; } //------------------------------------------------------------------------------ // converts sorted 2-linked list into balanced binary tree //------------------------------------------------------------------------------ TNode * // ret-middle of list (head of Btree) TNodeListSortable::ListSortedToTree( TNode * top // i/o-top of [sub]list to tree-ify ) { TNode * mid = top ,// middle of list * curr; int odd = 1; if ( top == NULL ) return NULL; for ( curr = top; curr; curr = curr->right ) // find list middle { if ( odd ^= 1 ) mid = mid->right; } if ( mid->left ) // split list around mid point { mid->left->right = NULL; // right terminate new sublist mid->left = ListSortedToTree(top); // recursive call to set left side } if ( mid->right ) { mid->right->left = NULL; // left terminate new sublist mid->right = ListSortedToTree(mid->right);// recursive call to set right side } return mid; } #pragma page() TNode * // ret-new head of tree TNodeListSortable::UnsortedToTree() { TNode * treehead = NULL, * tree, * curr, * next; MCSASSERTSZ( !IsTree(), "TNodeListSortable::UnsortedToTree - list is already a tree" ); if ( !IsTree() ) { for ( curr = head; curr; curr = next )// insert each node into BinTree { next = curr->right; // save right pointer curr->right = curr->left = NULL; // break chains for insertion node if ( treehead == NULL ) treehead = curr; // first node become BinTree head else { for ( tree = treehead; ; ) // iterative BinTree insert algorithm { if ( PCompare(curr, tree) <=0 )// if belongs left of current node if ( tree->left == NULL ) // if left tree empty { tree->left = curr; // insert here break; // and process right node } else // else tree = tree->left; // go down left side 1 level else // must be right side { if ( tree->right == NULL ) { tree->right = curr; break; } else tree = tree->right; } } } } TypeSetTree(); } return treehead; } #pragma page() //------------------------------------------------------------------------------ // comparison function used for scrambling a sorted linked list //------------------------------------------------------------------------------ TNodeCompare(ScrambledCompare) { return (rand() - RAND_MAX/2); } //------------------------------------------------------------------------------ // converts sorted 2-linked list into a scrambled random binary tree //------------------------------------------------------------------------------ void TNodeListSortable::SortedToScrambledTree() { MCSASSERTSZ( !IsTree(), "TNodeListSortable::SortedToScrambledTree - list is already a tree" ); if ( !IsTree() ) { TNodeCompare((*pOldCompare)); pOldCompare = PCompare; CompareSet(ScrambledCompare); UnsortedToTree(); CompareSet(pOldCompare); } } #pragma page() TNodeList::~TNodeList() { // _ASSERTE( (count == 0) && (head == NULL) ); if ( (count == 0) && (head == NULL) ) ; else { //printf( "\aTNodeList destructor failure - list is not empty!\a\n" ); } } void TNodeList::InsertTop( TNode * eIns // i/o-element to be inserted ) { MCSVERIFY(this); MCSVERIFY(eIns); eIns->right = head; eIns->left = NULL; if ( head ) head->left = eIns; else tail = eIns; head = eIns; count++; return; } void TNodeList::InsertBottom( TNode * eIns // i/o-element to be inserted ) { MCSVERIFY(this); MCSVERIFY(eIns); eIns->right = NULL; eIns->left = tail; if ( tail ) tail->right = eIns; else head = eIns; tail = eIns; count++; return; } void TNodeList::InsertAfter( TNode * eIns ,// i/o-element to be inserted TNode * eAft // i/o-element insert point ) { TNode * eFwd; // element after inserted element MCSVERIFY(this); MCSVERIFY(eIns); if ( !eAft ) InsertTop( eIns ); else { eFwd = eAft->right; eIns->right = eFwd; eIns->left = eAft; if ( eFwd ) eFwd->left = eIns; else tail = eIns; eAft->right = eIns; count++; } } void TNodeList::InsertBefore( TNode * eIns ,// i/o-element to be inserted TNode * eBef // i/o-element insert point ) { TNode * eBwd; // element before inserted element MCSVERIFY(this); MCSVERIFY(eIns); if ( !eBef ) InsertBottom( eIns ); else { eBwd = eBef->left; eIns->right = eBef; eIns->left = eBwd; if ( eBwd ) eBwd->right = eIns; else head = eIns; eBef->left = eIns; count++; } return; } void TNodeList::Remove( TNode const * t // i/o-new node to remove from list but not delete ) { MCSVERIFY(this); MCSVERIFY(t); if ( t->left ) t->left->right = t->right; else head = t->right; if ( t->right ) t->right->left = t->left; else tail = t->left; count--; //Remove links to the list from t. We cant do this because // t is a const * //t->left = t->right = NULL; } void TNodeList::Reverse() { TNode * node; TNode * swap; MCSVERIFY(this); for ( node = head; node; node = node->left ) { swap = node->left; node->left = node->right; node->right = swap; } swap = head; head = tail; tail = swap; } TNode * TNodeList::Find( TNodeCompareValue( (* Compare) ) ,// in -compares value in TNode to other value void const * findval ) const { TNode * curr; MCSASSERT(this); for ( curr = head; curr; curr = curr->right ) { if ( !Compare( curr, findval ) ) break; } return curr; } BOOL // ret-TRUE if valid TNodeListSortable::CountTree( TNode * pCurrentTop ,// i/o-top of [sub]tree to count nodes DWORD * pCount // i/o-Number of nodes encountered in the tree ) { if ( !pCurrentTop ) return TRUE; (*pCount)++; if( (*pCount) > count ) return FALSE; if(!CountTree(pCurrentTop->left,pCount)) return FALSE; if(!CountTree(pCurrentTop->right,pCount)) return FALSE; return TRUE; } BOOL // TRUE if Valid and FALSE if not TNodeListSortable::ValidateTree() { DWORD dwTempCount=0; DWORD bValid; MCSVERIFY(listType == TNodeTypeTree); bValid = CountTree(head,&dwTempCount); return bValid; } // Routine to validate the state of the list DWORD TNodeList::Validate( TNode ** pErrorNode ) { DWORD dwError=0; DWORD nNodesVisited=0; TNode * pCurrentNode; DWORD dwNodeCount = Count(); if(pErrorNode) *pErrorNode = NULL; #ifndef WIN16_VERSION try { #endif pCurrentNode = head; if ( pCurrentNode) // If the list is not empty { if ( pCurrentNode->left) { dwError = MCS_ListError_InvalidHead; } else { while ( pCurrentNode->right ) { if(pCurrentNode->right->left != pCurrentNode) { dwError = MCS_ListError_InvalidPtr; if(pErrorNode) *pErrorNode = pCurrentNode->right; break; } nNodesVisited++; if ( nNodesVisited > dwNodeCount ) { dwError = MCS_ListError_InvalidCount; break; } pCurrentNode = pCurrentNode->right; } if ( (!dwError) && (!pCurrentNode->right) ) { if ( pCurrentNode != tail) { dwError = MCS_ListError_InvalidTail; if(pErrorNode) *pErrorNode = pCurrentNode->right; } } } } else // if the list is empty { if(dwNodeCount) { dwError = MCS_ListError_InvalidCount; } } #ifndef WIN16_VERSION } catch(...) { dwError = MCS_ListError_Exception; } #endif return dwError; } void TNodeListSortable::TreeRemove( TNode * item // i/o-node to remove from binary tree ) { TNode ** prevNext = &head, * rep, * repLeft, * temp; int cmp; MCSVERIFY(listType == TNodeTypeTree); while ( *prevNext ) { cmp = PCompare( item, *prevNext ); if ( cmp < 0 ) prevNext = &(*prevNext)->left; else if ( cmp > 0 ) prevNext = &(*prevNext)->right; else { // we've found a matching 'name' (they compare equal) if ( *prevNext == item ) { // we've found the address we're looking for if ( (*prevNext)->right ) { rep = repLeft = (*prevNext)->right; for ( temp = rep->left; temp; temp = temp->left ) repLeft = temp; repLeft->left = (*prevNext)->left; temp = *prevNext; *prevNext = rep; } else { temp = *prevNext; *prevNext = (*prevNext)->left; // simple case } // break removed nodes links to existing tree temp->left = temp->right = NULL; count--; break; } } } return; } // returns the insert point in a sorted list for a prospective node TNode * // ret-insert before point or NULL TNodeListSortable::SortedFindInsertBefore( TNode * item ,// i/o-node to insert into TNode BOOL * exists // out-TRUE if already exists ) { int c; TNode * curr; *exists = FALSE; if ( !lastInsert ) { if ( !head ) // if null head, empty list, return NULL return NULL; lastInsert = head; } c = PCompare(item, lastInsert); if ( c < 0 ) lastInsert = head; for ( curr = lastInsert; curr; curr = curr->right ) { c = PCompare(item, curr); if ( c <= 0 ) if ( c == 0 ) *exists = TRUE; else break; } return curr; } // inserts node into sorted linear list void TNodeListSortable::SortedInsert( TNode * item // i/o-node to insert into TNode ) { BOOL exists; MCSVERIFY(listType != TNodeTypeTree); TNode * insertPoint = SortedFindInsertBefore(item, &exists); InsertBefore(item, insertPoint); lastInsert = item; } BOOL TNodeListSortable::SortedInsertIfNew( TNode * item // i/o-node to insert into TNode ) { BOOL exists; TNode * insertPoint = SortedFindInsertBefore(item, &exists); if ( !exists ) { InsertBefore(item, insertPoint); lastInsert = item; } return !exists; } void TNodeListSortable::TreeInsert( TNode * item ,// i/o-node to insert into binary tree short * depth // out-tree/recursion depth of new item ) { TNode ** prevNext = &head; int cmp; MCSVERIFY(listType == TNodeTypeTree); for ( *depth = 0; *prevNext; (*depth)++ ) { cmp = PCompare( item, *prevNext ); if ( cmp <= 0 ) prevNext = &(*prevNext)->left; else prevNext = &(*prevNext)->right; } *prevNext = item; item->left = item->right = NULL; count++; return; } TNode * TNodeListSortable::TreeFind( TNodeCompareValue( (* Compare) ) ,// in -compares value in TNode to other value void const * findval ) const { TNode * curr = head; int cmp; while ( curr ) { cmp = Compare( curr, findval ); if ( cmp > 0 ) curr = curr->left; else if ( cmp < 0 ) curr = curr->right; else // cmp == 0 break; } return curr; } TNode * // ret-TNode at pos n or NULL TNodeListOrdEnum::Get( long n // in -new position ) { long disCurr = n - nCurr, // distance to curr disTop = n < (long)list->Count()/2 ? n : n - list->Count(); #ifdef WIN16_VERSION long absDisTop = (disTop<0) ? -disTop : disTop; long absDisCurr = (disCurr<0) ? -disCurr : disCurr; if ( absDisTop < absDisCurr ) #else if ( abs(disTop) < abs(disCurr) ) #endif { Top(); disCurr = disTop; } if ( disCurr < 0 ) for ( Prev(); n < nCurr && Prev(); ); else for ( ; n > nCurr && Next(); ); return curr; } // returns the first node of the tree TNode * TNodeTreeEnum::First() { stackPos = stackBase; if ( top ) Push(top); return Next(); } // Returns the tree node logically following the value per the sort organization // specified by Compare, and sets up the enumeration to continue from that point. TNode * TNodeTreeEnum::FirstAfter( TNodeCompareValue( (* Compare) ) ,// in -compares value in TNode to other value void const * findVal // in -findVal to position after ) { TNode * tn; int cmp; stackPos = stackBase; for ( tn = top; tn; ) { Push(tn); cmp = Compare( tn, findVal ); if ( cmp < 0 ) { stackPos->state = Sright; if ( tn->right ) tn = tn->right; else return Next(); } else if ( cmp > 0 ) { stackPos->state = Sleft; if ( tn->left ) tn = tn->left; else { stackPos->state = Sused; return tn; } } else { stackPos->state = Sused; return Next(); } } return NULL; } // returns the Next logical node of the tree ending with NULL when complete TNode * TNodeTreeEnum::Next() { for ( ;; ) { switch ( stackPos->state ) { case Snone: // we've done nothing here stackPos->state = Sleft; if ( stackPos->save->left ) Push(stackPos->save->left); break; case Sleft: // we've gone left and are back stackPos->state = Sused; return stackPos->save; case Sused: // we've used the node stackPos->state = Sright; if ( stackPos->save->right ) Push(stackPos->save->right);// process right side of branch break; case Sright: // we've gone right and are back if ( !Pop() ) return NULL; break; case SComplete: return NULL; break; // Do we need this? default: // bad error MCSASSERT(FALSE); return NULL; } } return NULL; // can't get here } // Returns the address of the forward (left/right) pointer where the find node // already exists or would be inserted. If the singly deferenced result is not // null, the node's key value already exists in the tree. // If, after obtaining the insertion point, you want to insert the node, just // assign its address to the singly deferenced return value. The following inserts // the node "f" if it is not alread in the tree: // TNode **r = tree.TreeFindInsert(f); // if ( !*r ) // *r = f; TNode ** // ret-pointer forward pointer to find TNodeListSortable::TreeFindInsert( TNode const * find ,// in -node to find short * depth // out-tree depth of insertion point ) { TNode ** prevNext = &head; int cmp; for ( *depth = 0; *prevNext; (*depth)++ ) { cmp = PCompare( find, *prevNext ); if ( cmp < 0 ) prevNext = &(*prevNext)->left; else if ( cmp > 0 ) prevNext = &(*prevNext)->right; else break; } return prevNext; } // TNode.cpp - end of file