windows-nt/Source/XPSP1/NT/admin/admt/setdtct/tnode.cpp
2020-09-26 16:20:57 +08:00

811 lines
22 KiB
C++

//#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 <windows.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#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