Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

810 lines
22 KiB

  1. //#pragma title( "TNode.cpp - List/Tree base classes" )
  2. /*
  3. Copyright (c) 1995-1998, Mission Critical Software, Inc. All rights reserved.
  4. ===============================================================================
  5. Module - TNode.cpp
  6. System - Common
  7. Author - Tom Bernhardt
  8. Created - 1989-11-19
  9. Description - List/Tree base classes.
  10. TNode is a base class to define a collection element. It
  11. contains a left and right pointer to another TNode item and
  12. these may be organized as a double-linked linear list or
  13. binary tree in the collection classes that use TNode items.
  14. Central to its utility are member functions to convert between
  15. binary tree, sorted 2-way linear linked lists, and unsorted 2-way
  16. linked linear lists.
  17. Collection and enum classes
  18. TNodeList A simple collection of TNode elements.
  19. TNodeListSortable A TNodeList that is sortable by one or more compare functions.
  20. Conversion member functions for TNodeListSortable:
  21. The form of the list may be easily changed from binary tree to sorted list or
  22. vice versa. The following member functions support these transformations:
  23. ToSorted Converts the tree form into a sorted linear list form without
  24. need for comparisons; the order is preserved.
  25. SortedToTree Converts the sorted linear list form into a perfectly
  26. balanced binary tree without comparisons; the order is preserved.
  27. UnsortedToTree Converts the sorted linear list form into a binary tree
  28. that is not necesarily balanced. It uses the PCompare function
  29. to form the order of the tree. Thus if the list order closely
  30. matches the PCompare directed order, the resulting tree will be
  31. grossly unbalanced. This has a bearing on the performance and
  32. memory requirements of the ToSorted function which is recursive.
  33. So be careful, especially with large lists.
  34. Sort This resorts either a tree or list form according to the argument
  35. pCompare function pointer provided. Note the above admonition.
  36. In either form, exposed are also Insert and Remove member functions. The functions
  37. are wrappers for TreeInsert and SortedInsert function depending upon the current
  38. list type.
  39. Updates -
  40. 1995-05-01 TPB Converted to C++ classes.
  41. ===============================================================================
  42. */
  43. #ifdef USE_STDAFX
  44. # include "stdafx.h"
  45. #else
  46. # include <windows.h>
  47. #endif
  48. #include <stdlib.h>
  49. #include <stdio.h>
  50. #include <malloc.h>
  51. #include "TNode.hpp"
  52. #include "common.hpp"
  53. #pragma page()
  54. //------------------------------------------------------------------------------
  55. // Warning: Must not pass top == NULL
  56. //------------------------------------------------------------------------------
  57. TNode * // ret-head of sorted list
  58. TNodeListSortable::TreeToSortedList(
  59. TNode * top ,// i/o-top of [sub]tree to squash
  60. TNode ** newhead ,// out-leftmost branch from tree
  61. TNode ** newtail // out-rightmost branch from tree
  62. )
  63. {
  64. TNode * temp; // temporary pointer placeholder
  65. if ( top->left == NULL )
  66. *newhead = top; // this is leftmost of parent node
  67. else
  68. {
  69. TreeToSortedList(top->left, newhead, &temp);
  70. top->left = temp; // left = tail of sub-list
  71. top->left->right = top;
  72. }
  73. if ( top->right == NULL )
  74. *newtail = top; // tree is rightmost of parent node
  75. else
  76. {
  77. TreeToSortedList(top->right, &temp, newtail);
  78. top->right = temp; // right = head of sub-list
  79. top->right->left = top;
  80. }
  81. return *newhead;
  82. }
  83. //------------------------------------------------------------------------------
  84. // converts sorted 2-linked list into balanced binary tree
  85. //------------------------------------------------------------------------------
  86. TNode * // ret-middle of list (head of Btree)
  87. TNodeListSortable::ListSortedToTree(
  88. TNode * top // i/o-top of [sub]list to tree-ify
  89. )
  90. {
  91. TNode * mid = top ,// middle of list
  92. * curr;
  93. int odd = 1;
  94. if ( top == NULL )
  95. return NULL;
  96. for ( curr = top; curr; curr = curr->right ) // find list middle
  97. {
  98. if ( odd ^= 1 )
  99. mid = mid->right;
  100. }
  101. if ( mid->left ) // split list around mid point
  102. {
  103. mid->left->right = NULL; // right terminate new sublist
  104. mid->left = ListSortedToTree(top); // recursive call to set left side
  105. }
  106. if ( mid->right )
  107. {
  108. mid->right->left = NULL; // left terminate new sublist
  109. mid->right = ListSortedToTree(mid->right);// recursive call to set right side
  110. }
  111. return mid;
  112. }
  113. #pragma page()
  114. TNode * // ret-new head of tree
  115. TNodeListSortable::UnsortedToTree()
  116. {
  117. TNode * treehead = NULL,
  118. * tree,
  119. * curr,
  120. * next;
  121. MCSASSERTSZ( !IsTree(), "TNodeListSortable::UnsortedToTree - list is already a tree" );
  122. if ( !IsTree() )
  123. {
  124. for ( curr = head; curr; curr = next )// insert each node into BinTree
  125. {
  126. next = curr->right; // save right pointer
  127. curr->right = curr->left = NULL; // break chains for insertion node
  128. if ( treehead == NULL )
  129. treehead = curr; // first node become BinTree head
  130. else
  131. {
  132. for ( tree = treehead; ; ) // iterative BinTree insert algorithm
  133. {
  134. if ( PCompare(curr, tree) <=0 )// if belongs left of current node
  135. if ( tree->left == NULL ) // if left tree empty
  136. {
  137. tree->left = curr; // insert here
  138. break; // and process right node
  139. }
  140. else // else
  141. tree = tree->left; // go down left side 1 level
  142. else // must be right side
  143. {
  144. if ( tree->right == NULL )
  145. {
  146. tree->right = curr;
  147. break;
  148. }
  149. else
  150. tree = tree->right;
  151. }
  152. }
  153. }
  154. }
  155. TypeSetTree();
  156. }
  157. return treehead;
  158. }
  159. #pragma page()
  160. //------------------------------------------------------------------------------
  161. // comparison function used for scrambling a sorted linked list
  162. //------------------------------------------------------------------------------
  163. TNodeCompare(ScrambledCompare)
  164. {
  165. return (rand() - RAND_MAX/2);
  166. }
  167. //------------------------------------------------------------------------------
  168. // converts sorted 2-linked list into a scrambled random binary tree
  169. //------------------------------------------------------------------------------
  170. void
  171. TNodeListSortable::SortedToScrambledTree()
  172. {
  173. MCSASSERTSZ( !IsTree(), "TNodeListSortable::SortedToScrambledTree - list is already a tree" );
  174. if ( !IsTree() )
  175. {
  176. TNodeCompare((*pOldCompare));
  177. pOldCompare = PCompare;
  178. CompareSet(ScrambledCompare);
  179. UnsortedToTree();
  180. CompareSet(pOldCompare);
  181. }
  182. }
  183. #pragma page()
  184. TNodeList::~TNodeList()
  185. {
  186. // _ASSERTE( (count == 0) && (head == NULL) );
  187. if ( (count == 0) && (head == NULL) )
  188. ;
  189. else
  190. {
  191. //printf( "\aTNodeList destructor failure - list is not empty!\a\n" );
  192. }
  193. }
  194. void
  195. TNodeList::InsertTop(
  196. TNode * eIns // i/o-element to be inserted
  197. )
  198. {
  199. MCSVERIFY(this);
  200. MCSVERIFY(eIns);
  201. eIns->right = head;
  202. eIns->left = NULL;
  203. if ( head )
  204. head->left = eIns;
  205. else
  206. tail = eIns;
  207. head = eIns;
  208. count++;
  209. return;
  210. }
  211. void
  212. TNodeList::InsertBottom(
  213. TNode * eIns // i/o-element to be inserted
  214. )
  215. {
  216. MCSVERIFY(this);
  217. MCSVERIFY(eIns);
  218. eIns->right = NULL;
  219. eIns->left = tail;
  220. if ( tail )
  221. tail->right = eIns;
  222. else
  223. head = eIns;
  224. tail = eIns;
  225. count++;
  226. return;
  227. }
  228. void
  229. TNodeList::InsertAfter(
  230. TNode * eIns ,// i/o-element to be inserted
  231. TNode * eAft // i/o-element insert point
  232. )
  233. {
  234. TNode * eFwd; // element after inserted element
  235. MCSVERIFY(this);
  236. MCSVERIFY(eIns);
  237. if ( !eAft )
  238. InsertTop( eIns );
  239. else
  240. {
  241. eFwd = eAft->right;
  242. eIns->right = eFwd;
  243. eIns->left = eAft;
  244. if ( eFwd )
  245. eFwd->left = eIns;
  246. else
  247. tail = eIns;
  248. eAft->right = eIns;
  249. count++;
  250. }
  251. }
  252. void
  253. TNodeList::InsertBefore(
  254. TNode * eIns ,// i/o-element to be inserted
  255. TNode * eBef // i/o-element insert point
  256. )
  257. {
  258. TNode * eBwd; // element before inserted element
  259. MCSVERIFY(this);
  260. MCSVERIFY(eIns);
  261. if ( !eBef )
  262. InsertBottom( eIns );
  263. else
  264. {
  265. eBwd = eBef->left;
  266. eIns->right = eBef;
  267. eIns->left = eBwd;
  268. if ( eBwd )
  269. eBwd->right = eIns;
  270. else
  271. head = eIns;
  272. eBef->left = eIns;
  273. count++;
  274. }
  275. return;
  276. }
  277. void
  278. TNodeList::Remove(
  279. TNode const * t // i/o-new node to remove from list but not delete
  280. )
  281. {
  282. MCSVERIFY(this);
  283. MCSVERIFY(t);
  284. if ( t->left )
  285. t->left->right = t->right;
  286. else
  287. head = t->right;
  288. if ( t->right )
  289. t->right->left = t->left;
  290. else
  291. tail = t->left;
  292. count--;
  293. //Remove links to the list from t. We cant do this because
  294. // t is a const *
  295. //t->left = t->right = NULL;
  296. }
  297. void
  298. TNodeList::Reverse()
  299. {
  300. TNode * node;
  301. TNode * swap;
  302. MCSVERIFY(this);
  303. for ( node = head; node; node = node->left )
  304. {
  305. swap = node->left;
  306. node->left = node->right;
  307. node->right = swap;
  308. }
  309. swap = head;
  310. head = tail;
  311. tail = swap;
  312. }
  313. TNode *
  314. TNodeList::Find(
  315. TNodeCompareValue( (* Compare) ) ,// in -compares value in TNode to other value
  316. void const * findval
  317. ) const
  318. {
  319. TNode * curr;
  320. MCSASSERT(this);
  321. for ( curr = head; curr; curr = curr->right )
  322. {
  323. if ( !Compare( curr, findval ) )
  324. break;
  325. }
  326. return curr;
  327. }
  328. BOOL // ret-TRUE if valid
  329. TNodeListSortable::CountTree(
  330. TNode * pCurrentTop ,// i/o-top of [sub]tree to count nodes
  331. DWORD * pCount // i/o-Number of nodes encountered in the tree
  332. )
  333. {
  334. if ( !pCurrentTop )
  335. return TRUE;
  336. (*pCount)++;
  337. if( (*pCount) > count )
  338. return FALSE;
  339. if(!CountTree(pCurrentTop->left,pCount))
  340. return FALSE;
  341. if(!CountTree(pCurrentTop->right,pCount))
  342. return FALSE;
  343. return TRUE;
  344. }
  345. BOOL // TRUE if Valid and FALSE if not
  346. TNodeListSortable::ValidateTree()
  347. {
  348. DWORD dwTempCount=0;
  349. DWORD bValid;
  350. MCSVERIFY(listType == TNodeTypeTree);
  351. bValid = CountTree(head,&dwTempCount);
  352. return bValid;
  353. }
  354. // Routine to validate the state of the list
  355. DWORD
  356. TNodeList::Validate(
  357. TNode ** pErrorNode
  358. )
  359. {
  360. DWORD dwError=0;
  361. DWORD nNodesVisited=0;
  362. TNode * pCurrentNode;
  363. DWORD dwNodeCount = Count();
  364. if(pErrorNode)
  365. *pErrorNode = NULL;
  366. #ifndef WIN16_VERSION
  367. try
  368. {
  369. #endif
  370. pCurrentNode = head;
  371. if ( pCurrentNode) // If the list is not empty
  372. {
  373. if ( pCurrentNode->left)
  374. {
  375. dwError = MCS_ListError_InvalidHead;
  376. }
  377. else
  378. {
  379. while ( pCurrentNode->right )
  380. {
  381. if(pCurrentNode->right->left != pCurrentNode)
  382. {
  383. dwError = MCS_ListError_InvalidPtr;
  384. if(pErrorNode)
  385. *pErrorNode = pCurrentNode->right;
  386. break;
  387. }
  388. nNodesVisited++;
  389. if ( nNodesVisited > dwNodeCount )
  390. {
  391. dwError = MCS_ListError_InvalidCount;
  392. break;
  393. }
  394. pCurrentNode = pCurrentNode->right;
  395. }
  396. if ( (!dwError) && (!pCurrentNode->right) )
  397. {
  398. if ( pCurrentNode != tail)
  399. {
  400. dwError = MCS_ListError_InvalidTail;
  401. if(pErrorNode)
  402. *pErrorNode = pCurrentNode->right;
  403. }
  404. }
  405. }
  406. }
  407. else // if the list is empty
  408. {
  409. if(dwNodeCount)
  410. {
  411. dwError = MCS_ListError_InvalidCount;
  412. }
  413. }
  414. #ifndef WIN16_VERSION
  415. }
  416. catch(...)
  417. {
  418. dwError = MCS_ListError_Exception;
  419. }
  420. #endif
  421. return dwError;
  422. }
  423. void
  424. TNodeListSortable::TreeRemove(
  425. TNode * item // i/o-node to remove from binary tree
  426. )
  427. {
  428. TNode ** prevNext = &head,
  429. * rep,
  430. * repLeft,
  431. * temp;
  432. int cmp;
  433. MCSVERIFY(listType == TNodeTypeTree);
  434. while ( *prevNext )
  435. {
  436. cmp = PCompare( item, *prevNext );
  437. if ( cmp < 0 )
  438. prevNext = &(*prevNext)->left;
  439. else if ( cmp > 0 )
  440. prevNext = &(*prevNext)->right;
  441. else
  442. {
  443. // we've found a matching 'name' (they compare equal)
  444. if ( *prevNext == item )
  445. {
  446. // we've found the address we're looking for
  447. if ( (*prevNext)->right )
  448. {
  449. rep = repLeft = (*prevNext)->right;
  450. for ( temp = rep->left; temp; temp = temp->left )
  451. repLeft = temp;
  452. repLeft->left = (*prevNext)->left;
  453. temp = *prevNext;
  454. *prevNext = rep;
  455. }
  456. else
  457. {
  458. temp = *prevNext;
  459. *prevNext = (*prevNext)->left; // simple case
  460. }
  461. // break removed nodes links to existing tree
  462. temp->left = temp->right = NULL;
  463. count--;
  464. break;
  465. }
  466. }
  467. }
  468. return;
  469. }
  470. // returns the insert point in a sorted list for a prospective node
  471. TNode * // ret-insert before point or NULL
  472. TNodeListSortable::SortedFindInsertBefore(
  473. TNode * item ,// i/o-node to insert into TNode
  474. BOOL * exists // out-TRUE if already exists
  475. )
  476. {
  477. int c;
  478. TNode * curr;
  479. *exists = FALSE;
  480. if ( !lastInsert )
  481. {
  482. if ( !head ) // if null head, empty list, return NULL
  483. return NULL;
  484. lastInsert = head;
  485. }
  486. c = PCompare(item, lastInsert);
  487. if ( c < 0 )
  488. lastInsert = head;
  489. for ( curr = lastInsert; curr; curr = curr->right )
  490. {
  491. c = PCompare(item, curr);
  492. if ( c <= 0 )
  493. if ( c == 0 )
  494. *exists = TRUE;
  495. else
  496. break;
  497. }
  498. return curr;
  499. }
  500. // inserts node into sorted linear list
  501. void
  502. TNodeListSortable::SortedInsert(
  503. TNode * item // i/o-node to insert into TNode
  504. )
  505. {
  506. BOOL exists;
  507. MCSVERIFY(listType != TNodeTypeTree);
  508. TNode * insertPoint = SortedFindInsertBefore(item, &exists);
  509. InsertBefore(item, insertPoint);
  510. lastInsert = item;
  511. }
  512. BOOL
  513. TNodeListSortable::SortedInsertIfNew(
  514. TNode * item // i/o-node to insert into TNode
  515. )
  516. {
  517. BOOL exists;
  518. TNode * insertPoint = SortedFindInsertBefore(item, &exists);
  519. if ( !exists )
  520. {
  521. InsertBefore(item, insertPoint);
  522. lastInsert = item;
  523. }
  524. return !exists;
  525. }
  526. void
  527. TNodeListSortable::TreeInsert(
  528. TNode * item ,// i/o-node to insert into binary tree
  529. short * depth // out-tree/recursion depth of new item
  530. )
  531. {
  532. TNode ** prevNext = &head;
  533. int cmp;
  534. MCSVERIFY(listType == TNodeTypeTree);
  535. for ( *depth = 0; *prevNext; (*depth)++ )
  536. {
  537. cmp = PCompare( item, *prevNext );
  538. if ( cmp <= 0 )
  539. prevNext = &(*prevNext)->left;
  540. else
  541. prevNext = &(*prevNext)->right;
  542. }
  543. *prevNext = item;
  544. item->left = item->right = NULL;
  545. count++;
  546. return;
  547. }
  548. TNode *
  549. TNodeListSortable::TreeFind(
  550. TNodeCompareValue( (* Compare) ) ,// in -compares value in TNode to other value
  551. void const * findval
  552. ) const
  553. {
  554. TNode * curr = head;
  555. int cmp;
  556. while ( curr )
  557. {
  558. cmp = Compare( curr, findval );
  559. if ( cmp > 0 )
  560. curr = curr->left;
  561. else if ( cmp < 0 )
  562. curr = curr->right;
  563. else // cmp == 0
  564. break;
  565. }
  566. return curr;
  567. }
  568. TNode * // ret-TNode at pos n or NULL
  569. TNodeListOrdEnum::Get(
  570. long n // in -new position
  571. )
  572. {
  573. long disCurr = n - nCurr, // distance to curr
  574. disTop = n < (long)list->Count()/2 ? n : n - list->Count();
  575. #ifdef WIN16_VERSION
  576. long absDisTop = (disTop<0) ? -disTop : disTop;
  577. long absDisCurr = (disCurr<0) ? -disCurr : disCurr;
  578. if ( absDisTop < absDisCurr )
  579. #else
  580. if ( abs(disTop) < abs(disCurr) )
  581. #endif
  582. {
  583. Top();
  584. disCurr = disTop;
  585. }
  586. if ( disCurr < 0 )
  587. for ( Prev(); n < nCurr && Prev(); );
  588. else
  589. for ( ; n > nCurr && Next(); );
  590. return curr;
  591. }
  592. // returns the first node of the tree
  593. TNode *
  594. TNodeTreeEnum::First()
  595. {
  596. stackPos = stackBase;
  597. if ( top )
  598. Push(top);
  599. return Next();
  600. }
  601. // Returns the tree node logically following the value per the sort organization
  602. // specified by Compare, and sets up the enumeration to continue from that point.
  603. TNode *
  604. TNodeTreeEnum::FirstAfter(
  605. TNodeCompareValue( (* Compare) ) ,// in -compares value in TNode to other value
  606. void const * findVal // in -findVal to position after
  607. )
  608. {
  609. TNode * tn;
  610. int cmp;
  611. stackPos = stackBase;
  612. for ( tn = top; tn; )
  613. {
  614. Push(tn);
  615. cmp = Compare( tn, findVal );
  616. if ( cmp < 0 )
  617. {
  618. stackPos->state = Sright;
  619. if ( tn->right )
  620. tn = tn->right;
  621. else
  622. return Next();
  623. }
  624. else if ( cmp > 0 )
  625. {
  626. stackPos->state = Sleft;
  627. if ( tn->left )
  628. tn = tn->left;
  629. else
  630. {
  631. stackPos->state = Sused;
  632. return tn;
  633. }
  634. }
  635. else
  636. {
  637. stackPos->state = Sused;
  638. return Next();
  639. }
  640. }
  641. return NULL;
  642. }
  643. // returns the Next logical node of the tree ending with NULL when complete
  644. TNode *
  645. TNodeTreeEnum::Next()
  646. {
  647. for ( ;; )
  648. {
  649. switch ( stackPos->state )
  650. {
  651. case Snone: // we've done nothing here
  652. stackPos->state = Sleft;
  653. if ( stackPos->save->left )
  654. Push(stackPos->save->left);
  655. break;
  656. case Sleft: // we've gone left and are back
  657. stackPos->state = Sused;
  658. return stackPos->save;
  659. case Sused: // we've used the node
  660. stackPos->state = Sright;
  661. if ( stackPos->save->right )
  662. Push(stackPos->save->right);// process right side of branch
  663. break;
  664. case Sright: // we've gone right and are back
  665. if ( !Pop() )
  666. return NULL;
  667. break;
  668. case SComplete:
  669. return NULL;
  670. break; // Do we need this?
  671. default: // bad error
  672. MCSASSERT(FALSE);
  673. return NULL;
  674. }
  675. }
  676. return NULL; // can't get here
  677. }
  678. // Returns the address of the forward (left/right) pointer where the find node
  679. // already exists or would be inserted. If the singly deferenced result is not
  680. // null, the node's key value already exists in the tree.
  681. // If, after obtaining the insertion point, you want to insert the node, just
  682. // assign its address to the singly deferenced return value. The following inserts
  683. // the node "f" if it is not alread in the tree:
  684. // TNode **r = tree.TreeFindInsert(f);
  685. // if ( !*r )
  686. // *r = f;
  687. TNode ** // ret-pointer forward pointer to find
  688. TNodeListSortable::TreeFindInsert(
  689. TNode const * find ,// in -node to find
  690. short * depth // out-tree depth of insertion point
  691. )
  692. {
  693. TNode ** prevNext = &head;
  694. int cmp;
  695. for ( *depth = 0; *prevNext; (*depth)++ )
  696. {
  697. cmp = PCompare( find, *prevNext );
  698. if ( cmp < 0 )
  699. prevNext = &(*prevNext)->left;
  700. else if ( cmp > 0 )
  701. prevNext = &(*prevNext)->right;
  702. else
  703. break;
  704. }
  705. return prevNext;
  706. }
  707. // TNode.cpp - end of file