source: trunk/src/helpers/tree.c@ 56

Last change on this file since 56 was 56, checked in by umoeller, 24 years ago

misc changes

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 34.0 KB
Line 
1
2/*
3 *@@sourcefile tree.c:
4 * contains helper functions for maintaining 'Red-Black' balanced
5 * binary trees.
6 * See explanations below.
7 *
8 * This file is all new with V0.9.5 (2000-09-29) [umoeller].
9 *
10 * Usage: All C programs; not OS/2-specific.
11 *
12 * Function prefixes (new with V0.81):
13 * -- tree* tree helper functions
14 *
15 * This has been taken from the Standard Function Library (SFL)
16 * by iMatix Corporation and changed to user the "id" member for
17 * tree sorting/comparison. This implementation is released
18 * under the GPL.
19 *
20 * <B>Introduction</B>
21 *
22 * Binary trees are different from linked lists in that items
23 * are not simply linked sequentially, but instead put into
24 * a tree-like structure.
25 *
26 * While lists have "next" and "previous" pointers, trees have
27 * "left" and "right" pointers which keep the tree sorted at
28 * all times.
29 *
30 * Per definition, in our trees, if you follow the "left" pointer,
31 * you will reach an item which is "greater than" the current node.
32 * Reversely, following the "right" pointer will lead you to a
33 * node which is "less than" the current node.
34 *
35 * What is considered "less" or "greater" is determined by a
36 * comparison callback to be supplied by the caller.
37 *
38 * For this, the functions here use the TREE structure. You can
39 * easily see that this has the "left" and "right" members,
40 * which make up the tree.
41 *
42 * In addition, each tree has a "tree root" item, from which all
43 * other tree nodes can be reached by following the "left" and
44 * "right" pointers.
45 *
46 * The implementation here has the following characteristics:
47 *
48 * -- We have "binary" trees. That is, there are only "left" and
49 * "right" pointers.
50 *
51 * -- The tree is always "balanced". The tree gets completely
52 * reordered when items are added/removed to ensure that
53 * all paths through the tree are approximately the same
54 * length. This avoids the "worst case" scenario that some
55 * paths grow terribly long while others remain short, which
56 * can make searching very inefficient.
57 *
58 * -- The tree nodes are marked as either "red" or "black", which
59 * is an algorithm to allow the implementation of 2-3-4 trees
60 * using a binary tree only. I don't fully understand how this
61 * works, but essentially, "red" nodes represent a 3 or 4 node,
62 * while "black" nodes are plain binary nodes.
63 *
64 * As much as I understand about all this, red-black balanced
65 * binary trees are the most efficient tree algorithm known to
66 * mankind. As long as you are sure that trees are more efficient
67 * in your situation than a linked list in the first place (see
68 * below for comparisons), use the functions in here.
69 *
70 * <B>Using binary trees</B>
71 *
72 * You can use any structure as elements in a tree, provided
73 * that the first member in the structure is a TREE structure
74 * (i.e. it has the left, right, parent, id, and colour members).
75 * The tree functions don't care what follows, since they do
76 * not manage any memory themselves.
77 *
78 * So the implementation here is slightly different from the
79 * linked lists in linklist.c, because the LISTNODE structs
80 * only have pointers to the data. By contrast, the TREE structs
81 * are expected to contain the data themselves. See treeInsertID()
82 * for a sample.
83 *
84 * Initialize the root of the tree with treeInit(). Then
85 * add nodes to the tree with treeInsertID() and remove nodes
86 * with treeDelete().
87 *
88 * You can test whether a tree is empty by comparing its
89 * root with TREE_NULL.
90 *
91 * Most functions in here come in two flavors.
92 *
93 * -- You can provide a comparison function and use the "Node"
94 * flavors of these functions. This is useful, for example,
95 * if you are storing strings. You can then write a short
96 * comparison function which does a strcmp() on the data
97 * of tree nodes.
98 *
99 * The order of nodes in the tree is determined by calling a
100 * node comparison function provided by the caller
101 * (which you must write). This must be declared as:
102 *
103 + int TREEENTRY fnMyCompareNodes(TREE *t1, TREE *t2);
104 *
105 * It obviously receives two TREE pointers, which it must
106 * compare and return:
107 *
108 + 0: tree1 == tree2
109 + -1: tree1 < tree2
110 + +1: tree1 > tree2
111 *
112 * -- The "ID" functions (e.g. treeInsertID) do not require
113 * a comparison function, but will use the "id" member of
114 * the TREE structure instead. If this flavor is used, an
115 * internal comparison function is used for comparing the
116 * "id" fields, which are assumed to be plain ULONGs.
117 *
118 * <B>Trees vs. linked lists</B>
119 *
120 * Compared to linked lists (as implemented by linklist.c),
121 * trees allow for much faster searching.
122 *
123 * Assuming a linked list contains N items, then searching a
124 * linked list for an item will take an average of N/2 comparisons
125 * and even N comparisons if the item cannot be found (unless
126 * you keep the list sorted, but linklist.c doesn't do this).
127 *
128 * According to "Algorithms in C", a search in a balanced
129 * "red-black" binary tree takes about lg N comparisons on
130 * average, and insertions take less than one rotation on
131 * average.
132 *
133 * Example: You need to build a list of files, and you
134 * will search the list frequently according to the file
135 * handles. This would make the handle an ideal "id" field.
136 *
137 * Differences compared to linklist.c:
138 *
139 * -- Trees are considerably slower when inserting and removing
140 * nodes because the tree has to be rebalanced every time
141 * a node changes. By contrast, trees are much faster finding
142 * nodes because the tree is always sorted.
143 *
144 * -- If you are not using the "ID" flavors, you must supply a
145 * comparison function to allow the tree functions to sort the tree.
146 *
147 * -- As opposed to a LISTNODE, the TREE structure (which
148 * represents a tree node) does not contain a data pointer,
149 * as said above. The caller must do all memory management.
150 *
151 *@@added V0.9.5 (2000-09-29) [umoeller]
152 *@@header "helpers\tree.h"
153 */
154
155/*
156 * Written: 97/11/18 Jonathan Schultz <jonathan@imatix.com>
157 * Revised: 98/12/08 Jonathan Schultz <jonathan@imatix.com>
158 *
159 * Copyright (C) 1991-99 iMatix Corporation.
160 * Copyright (C) 2000 Ulrich M”ller.
161 * This file is part of the "XWorkplace helpers" source package.
162 * This is free software; you can redistribute it and/or modify
163 * it under the terms of the GNU General Public License as published
164 * by the Free Software Foundation, in version 2 as it comes in the
165 * "COPYING" file of the XWorkplace main distribution.
166 * This program is distributed in the hope that it will be useful,
167 * but WITHOUT ANY WARRANTY; without even the implied warranty of
168 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
169 * GNU General Public License for more details.
170 */
171
172/*
173 *@@category: Helpers\C helpers\Red-black balanced binary trees
174 * See tree.c.
175 */
176
177#include "setup.h"
178#include "helpers\tree.h"
179
180// Constants
181TREE TREE_EMPTY = {TREE_NULL, TREE_NULL, NULL, BLACK};
182
183// Internal function prototypes
184static void insert_fixup(TREE **root, TREE *tree);
185static void rotate_left(TREE **root, TREE *tree);
186static void rotate_right(TREE **root, TREE *tree);
187static void delete_fixup(TREE **root, TREE *tree);
188
189/*
190 *@@ treeInit:
191 * initializes an empty tree. The data on the
192 * tree will be invalid, and no memory will be
193 * freed.
194 *
195 * Usage:
196 + TREE *TreeRoot;
197 + treeInit(&TreeRoot);
198 */
199
200void treeInit(TREE **root)
201{
202 *root = TREE_NULL;
203}
204
205/*
206 * fnCompareIDs:
207 *
208 *added V0.9.9 (2001-02-06) [umoeller]
209 */
210
211int TREEENTRY fnCompareIDs(unsigned long id1, unsigned long id2)
212{
213 if (id1 < id2)
214 return -1;
215 if (id1 > id2)
216 return +1;
217 return (0);
218}
219
220/*
221 *@@ treeInsertID:
222 * inserts a node into an existing tree.
223 *
224 * Note: A tree node MUST contain a TREE structure
225 * at the beginning for the tree functions to work.
226 * So to create a tree node with usable data, do this:
227 *
228 + typedef _MYTREENODE
229 + {
230 + // TREE must be at beginning
231 + TREE Tree;
232 + // now use whatever you want
233 + CHAR szMyExtraData[100];
234 + } MYTREENODE, *PMYTREENODE;
235 *
236 * When calling the tree functions, manually cast your
237 * MYTREENODE pointers to (TREE*).
238 *
239 * This function initialises the node pointers and colour
240 * in the TREE structure to correct values, so the caller
241 * does not have to worry about those.
242 *
243 * However you must initialize the TREE.id member correctly
244 * so that your comparison function can compare on that
245 * to find the correct place in the tree to insert the node.
246 *
247 * Usage:
248 + TREE *TreeRoot;
249 + treeInit(&TreeRoot);
250 +
251 + PMYTREENODE pTreeItem = malloc(sizeof(MYTREENODE));
252 + pTreeItem->Tree.id = 1;
253 +
254 + treeInsertID(&TreeRoot,
255 + (TREE*)pTreeItem,
256 + FALSE);
257 *
258 * Returns:
259 *
260 * -- TREE_OK: OK, item inserted.
261 *
262 * -- TREE_DUPLICATE: if (fAllowDuplicates == FALSE), this is
263 * returned if a tree item with the specified ID already
264 * exists.
265 *
266 *@@changed V0.9.9 (2001-02-06) [umoeller]: removed comparison func
267 */
268
269int treeInsertID(TREE **root, // in: root of tree
270 TREE *tree, // in: new tree node
271 BOOL fAllowDuplicates) // in: whether duplicates with the same ID are allowed
272{
273 TREE *current,
274 *parent;
275 int last_comp = 0;
276
277 // find where node belongs
278 current = *root;
279 parent = NULL;
280 while (current != TREE_NULL)
281 {
282 parent = current;
283 last_comp = fnCompareIDs(tree->id, current->id);
284 switch (last_comp)
285 {
286 case -1: current = current->left; break;
287 case 1: current = current->right; break;
288 default:
289 if (fAllowDuplicates)
290 current = current->left;
291 else
292 return TREE_DUPLICATE;
293 }
294 }
295
296 // set up new node
297 ((TREE*)tree)->parent = parent;
298 ((TREE*)tree)->left = TREE_NULL;
299 ((TREE*)tree)->right = TREE_NULL;
300 ((TREE*)tree)->colour = RED;
301
302 // insert node in tree
303 if (parent)
304 switch (last_comp)
305 {
306 case 1: parent->right = tree; break;
307 default: parent->left = tree;
308 }
309 else
310 *root = tree;
311
312 insert_fixup(root, tree);
313 return(TREE_OK);
314}
315
316/*
317 *@@ treeInsertNode:
318 * similar to treeInsertID, but this uses
319 * a comparision function which compares
320 * nodes.
321 */
322
323int treeInsertNode(TREE **root, // in: root of tree
324 TREE *tree, // in: new tree node
325 FNTREE_COMPARE_NODES *comp, // in: comparison function
326 BOOL fAllowDuplicates) // in: whether duplicates with the same ID are allowed
327{
328 TREE
329 *current,
330 *parent;
331 int
332 last_comp = 0;
333
334 // find where node belongs
335 current = *root;
336 parent = NULL;
337 while (current != TREE_NULL)
338 {
339 parent = current;
340 last_comp = comp(tree, current);
341 switch (last_comp)
342 {
343 case -1: current = current->left; break;
344 case 1: current = current->right; break;
345 default: if (fAllowDuplicates)
346 current = current->left;
347 else
348 return TREE_DUPLICATE;
349
350 }
351 }
352
353 // set up new node
354 ((TREE*)tree)->parent = parent;
355 ((TREE*)tree)->left = TREE_NULL;
356 ((TREE*)tree)->right = TREE_NULL;
357 ((TREE*)tree)->colour = RED;
358
359 // insert node in tree
360 if (parent)
361 switch (last_comp)
362 {
363 case 1: parent->right = tree; break;
364 default: parent->left = tree;
365 }
366 else
367 *root = tree;
368
369 insert_fixup(root, tree);
370 return(TREE_OK);
371}
372
373/*
374 * insert_fixup:
375 * maintains the Red-Black tree balance after a node has been inserted.
376 *
377 * Private function.
378 */
379
380static void insert_fixup(TREE **root,
381 TREE *tree)
382{
383 TREE *uncle;
384
385 // check red-black properties
386 while ((tree != *root)
387 && (tree->parent->colour == RED))
388 {
389 // we have a violation
390 if (tree->parent == tree->parent->parent->left)
391 {
392 uncle = tree->parent->parent->right;
393 if (uncle->colour == RED)
394 {
395 // uncle is RED
396 tree ->parent->colour = BLACK;
397 uncle->colour = BLACK;
398 tree ->parent->parent->colour = RED;
399
400 tree = tree->parent->parent;
401 }
402 else
403 {
404 // uncle is BLACK
405 if (tree == tree->parent->right)
406 {
407 // make tree a left child
408 tree = tree->parent;
409 rotate_left (root, tree);
410 }
411
412 // recolor and rotate
413 tree->parent->colour = BLACK;
414 tree->parent->parent->colour = RED;
415 rotate_right (root, tree->parent->parent);
416 }
417 }
418 else
419 {
420 // mirror image of above code
421 uncle = tree->parent->parent->left;
422 if (uncle->colour == RED)
423 {
424 // uncle is RED
425 tree ->parent->colour = BLACK;
426 uncle->colour = BLACK;
427 tree ->parent->parent->colour = RED;
428
429 tree = tree->parent->parent;
430 }
431 else
432 {
433 // uncle is BLACK
434 if (tree == tree->parent->left)
435 {
436 tree = tree->parent;
437 rotate_right (root, tree);
438 }
439 tree->parent->colour = BLACK;
440 tree->parent->parent->colour = RED;
441 rotate_left (root, tree->parent->parent);
442 }
443 }
444 }
445 (*root)->colour = BLACK;
446}
447
448/*
449 * rotate_left:
450 * rotates tree to left.
451 *
452 * Private function.
453 */
454
455static void rotate_left(TREE **root,
456 TREE *tree)
457{
458 TREE *other = tree->right;
459
460 // establish tree->right link
461 tree->right = other->left;
462 if (other->left != TREE_NULL)
463 other->left->parent = tree;
464
465 // establish other->parent link
466 if (other != TREE_NULL)
467 other->parent = tree->parent;
468
469 if (tree->parent)
470 {
471 if (tree == tree->parent->left)
472 tree->parent->left = other;
473 else
474 tree->parent->right = other;
475 }
476 else
477 *root = other;
478
479 // link tree and other
480 other->left = tree;
481 if (tree != TREE_NULL)
482 tree->parent = other;
483}
484
485/*
486 * rotate_right:
487 * rotates tree to right.
488 *
489 * Private function.
490 */
491
492static void rotate_right(TREE **root,
493 TREE *tree)
494{
495 TREE *other;
496
497 other = tree->left;
498
499 // establish tree->left link
500 tree->left = other->right;
501 if (other->right != TREE_NULL)
502 other->right->parent = tree;
503
504 // establish other->parent link
505 if (other != TREE_NULL)
506 other->parent = tree->parent;
507
508 if (tree->parent)
509 {
510 if (tree == tree->parent->right)
511 tree->parent->right = other;
512 else
513 tree->parent->left = other;
514 }
515 else
516 *root = other;
517
518 // link tree and other
519 other->right = tree;
520 if (tree != TREE_NULL)
521 tree->parent = other;
522}
523
524/*
525 *@@ treeDelete:
526 * deletes a node from a tree. Does not deallocate any memory.
527 *
528 * Returns:
529 *
530 * -- TREE_OK: node deleted.
531 * -- TREE_INVALID_NODE: tree node not found.
532 */
533
534int treeDelete(TREE **root, // in: root of tree
535 TREE *tree) // in: tree node to delete
536{
537 int irc = TREE_OK;
538
539 TREE
540 *youngest, *descendent;
541 TREE_COLOUR
542 colour;
543
544 if ( (!tree)
545 || (tree == TREE_NULL)
546 )
547 return TREE_INVALID_NODE;
548
549 if ( (((TREE*)tree)->left == TREE_NULL)
550 || (((TREE*)tree)->right == TREE_NULL)
551 )
552 // descendent has a TREE_NULL node as a child
553 descendent = tree;
554 else
555 {
556 // find tree successor with a TREE_NULL node as a child
557 descendent = ((TREE*)tree)->right;
558 while (descendent->left != TREE_NULL)
559 descendent = descendent->left;
560 }
561
562 // youngest is descendent's only child, if there is one, else TREE_NULL
563 if (descendent->left != TREE_NULL)
564 youngest = descendent->left;
565 else
566 youngest = descendent->right;
567
568 // remove descendent from the parent chain
569 if (youngest != TREE_NULL)
570 youngest->parent = descendent->parent;
571 if (descendent->parent)
572 {
573 if (descendent == descendent->parent->left)
574 descendent->parent->left = youngest;
575 else
576 descendent->parent->right = youngest;
577 }
578 else
579 *root = youngest;
580
581 colour = descendent->colour;
582
583 if (descendent != (TREE *) tree)
584 {
585 // Conceptually what we are doing here is moving the data from
586 // descendent to tree. In fact we do this by linking descendent
587 // into the structure in the place of tree.
588 descendent->left = ((TREE*)tree)->left;
589 descendent->right = ((TREE*)tree)->right;
590 descendent->parent = ((TREE*)tree)->parent;
591 descendent->colour = ((TREE*)tree)->colour;
592
593 if (descendent->parent)
594 {
595 if (tree == descendent->parent->left)
596 descendent->parent->left = descendent;
597 else
598 descendent->parent->right = descendent;
599 }
600 else
601 *root = descendent;
602
603 if (descendent->left != TREE_NULL)
604 descendent->left->parent = descendent;
605
606 if (descendent->right != TREE_NULL)
607 descendent->right->parent = descendent;
608 }
609
610 if ( (youngest != TREE_NULL)
611 && (colour == BLACK))
612 delete_fixup (root, youngest);
613
614 return (irc);
615}
616
617/*
618 *@@ delete_fixup:
619 * maintains Red-Black tree balance after deleting a node.
620 *
621 * Private function.
622 */
623
624static void delete_fixup(TREE **root,
625 TREE *tree)
626{
627 TREE
628 *sibling;
629
630 while (tree != *root && tree->colour == BLACK)
631 {
632 if (tree == tree->parent->left)
633 {
634 sibling = tree->parent->right;
635 if (sibling->colour == RED)
636 {
637 sibling->colour = BLACK;
638 tree->parent->colour = RED;
639 rotate_left (root, tree->parent);
640 sibling = tree->parent->right;
641 }
642 if ((sibling->left->colour == BLACK)
643 && (sibling->right->colour == BLACK))
644 {
645 sibling->colour = RED;
646 tree = tree->parent;
647 }
648 else
649 {
650 if (sibling->right->colour == BLACK)
651 {
652 sibling->left->colour = BLACK;
653 sibling->colour = RED;
654 rotate_right (root, sibling);
655 sibling = tree->parent->right;
656 }
657 sibling->colour = tree->parent->colour;
658 tree->parent->colour = BLACK;
659 sibling->right->colour = BLACK;
660 rotate_left (root, tree->parent);
661 tree = *root;
662 }
663 }
664 else
665 {
666 sibling = tree->parent->left;
667 if (sibling->colour == RED)
668 {
669 sibling->colour = BLACK;
670 tree->parent->colour = RED;
671 rotate_right (root, tree->parent);
672 sibling = tree->parent->left;
673 }
674 if ((sibling->right->colour == BLACK)
675 && (sibling->left->colour == BLACK))
676 {
677 sibling->colour = RED;
678 tree = tree->parent;
679 }
680 else
681 {
682 if (sibling->left->colour == BLACK)
683 {
684 sibling->right->colour = BLACK;
685 sibling->colour = RED;
686 rotate_left (root, sibling);
687 sibling = tree->parent->left;
688 }
689 sibling->colour = tree->parent->colour;
690 tree->parent->colour = BLACK;
691 sibling->left->colour = BLACK;
692 rotate_right (root, tree->parent);
693 tree = *root;
694 }
695 }
696 }
697 tree->colour = BLACK;
698}
699
700/*
701 *@@ treeFindEQID:
702 * finds a node with ID exactly matching that provided.
703 * Returns NULL if not found.
704 */
705
706void* treeFindEQID(TREE **root,
707 unsigned long id)
708{
709 TREE *current = *root,
710 *found = NULL;
711
712 while (current != TREE_NULL)
713 switch (fnCompareIDs(current->id, id))
714 {
715 case -1: current = current->right; break;
716 case 1: current = current->left; break;
717 default: found = current; // In case of duplicates,
718 current = current->left; // get the first one.
719 }
720
721 return found;
722}
723
724/*
725 *@@ treeFindGEID:
726 * finds a node with ID greater than or equal to provided.
727 * To find a tree node, your comparison function must
728 * compare the tree node IDs.
729 */
730
731void* treeFindGEID(TREE **root,
732 unsigned long idFind)
733{
734 TREE *current = *root,
735 *found;
736
737 found = NULL;
738 while (current != TREE_NULL)
739 switch (fnCompareIDs(current->id, idFind))
740 {
741 case -1: current = current->right; break;
742 default: found = current;
743 current = current->left;
744 }
745
746 return found;
747}
748
749/*
750 *@@ treeFindEQNode:
751 * finds a node with ID exactly matching that provided.
752 * To find a tree node, your comparison function must
753 * compare the tree nodes.
754 */
755
756void* treeFindEQNode(TREE **root,
757 TREE *nodeFind,
758 FNTREE_COMPARE_NODES *comp)
759{
760 TREE *current = *root,
761 *found;
762
763 found = NULL;
764 while (current != TREE_NULL)
765 switch (comp(current, nodeFind))
766 {
767 case -1: current = current->right; break;
768 case 1: current = current->left; break;
769 default: found = current; // In case of duplicates,
770 current = current->left; // get the first one.
771 }
772
773 return found;
774}
775
776/*
777 *@@ treeFindGENode:
778 * finds a node with ID greater than or equal to provided.
779 * To find a tree node, your comparison function must
780 * compare the tree nodes.
781 */
782
783void* treeFindGENode(TREE **root,
784 TREE *nodeFind,
785 FNTREE_COMPARE_NODES *comp)
786{
787 TREE *current = *root,
788 *found;
789
790 found = NULL;
791 while (current != TREE_NULL)
792 switch (comp(current, nodeFind))
793 {
794 case -1: current = current->right; break;
795 default: found = current;
796 current = current->left;
797 }
798
799 return found;
800}
801
802/*
803 *@@ treeFindLTNode:
804 * finds a node with Node less than provided.
805 * To find a tree node, your comparison function must
806 * compare the tree nodes.
807 */
808
809void* treeFindLTNode(TREE **root,
810 TREE *nodeFind,
811 FNTREE_COMPARE_NODES *comp)
812{
813 TREE *current = *root,
814 *found;
815
816 found = NULL;
817 while (current != TREE_NULL)
818 switch (comp(current, nodeFind))
819 {
820 case -1: found = current;
821 current = current->right; break;
822 default: current = current->left;
823 }
824
825 return found;
826}
827
828/*
829 *@@ treeFindLENode:
830 * finds a node with Node less than or equal to provided.
831 * To find a tree node, your comparison function must
832 * compare the tree nodes.
833 */
834
835void* treeFindLENode(TREE **root,
836 TREE *nodeFind,
837 FNTREE_COMPARE_NODES *comp)
838{
839 TREE *current = *root,
840 *found;
841
842 found = NULL;
843 while (current != TREE_NULL)
844 switch (comp(current, nodeFind))
845 {
846 case 1 : current = current->left; break;
847 default: found = current;
848 current = current->right;
849 }
850
851 return found;
852}
853
854/*
855 *@@ treeFindGTNode:
856 * finds a node with Node greater than provided.
857 * To find a tree node, your comparison function must
858 * compare the tree nodes.
859 */
860
861void* treeFindGTNode(TREE **root,
862 TREE *nodeFind,
863 FNTREE_COMPARE_NODES *comp)
864{
865 TREE *current = *root,
866 *found;
867
868 found = NULL;
869 while (current != TREE_NULL)
870 switch (comp(current, nodeFind))
871 {
872 case 1 : found = current;
873 current = current->left; break;
874 default: current = current->right;
875 }
876
877 return found;
878}
879
880/*
881 *@@ treeFindEQID:
882 * finds a node with data exactly matching that provided.
883 * To find a tree node, your comparison function must
884 * compare a tree member with external data.
885 *
886 * This is useful for finding a tree item from a string ID.
887 *
888 * Make sure to use treeInsertNode and compare according
889 * to a string member, and then write a second compare
890 * function for this function which compares the string
891 * member to an external string.
892 */
893
894void* treeFindEQData(TREE **root,
895 void *pData,
896 FNTREE_COMPARE_DATA *comp)
897{
898 TREE *current = *root,
899 *found = NULL;
900
901 while (current != TREE_NULL)
902 switch (comp(current, pData))
903 {
904 case -1: current = current->right; break;
905 case 1: current = current->left; break;
906 default: found = current; // In case of duplicates,
907 current = current->left; // get the first one.
908 }
909
910 return found;
911}
912
913/*
914 *@@ treeTraverse:
915 * traverses the specified tree, calling a processing function
916 * for each tree node.
917 *
918 * The processing function ("process") must be declared as
919 * follows:
920 *
921 + void fnProcess(TREE *t, // current tree node
922 + void *pUser); // user data
923 *
924 * and will receive the "pUser" parameter, which you can use
925 * as a data pointer to some structure for whatever you like.
926 *
927 * WARNING: This function recurses and can use up a lot of
928 * stack. For very deep trees, traverse the tree using
929 * treeFirst and treeNext instead. See treeNext for a sample.
930 *
931 * "method" specifies in which order the nodes are traversed.
932 * This can be:
933 *
934 * -- 1: current node first, then left node, then right node.
935 * -- 2: left node first, then right node, then current node.
936 * -- 0 or other: left node first, then current node, then right node.
937 * This is the sorted order.
938 */
939
940void treeTraverse(TREE *tree, // in: root of tree
941 TREE_PROCESS *process, // in: callback for each node
942 void *pUser, // in: user param for callback
943 int method) // in: traversal mode
944{
945 if ( (!tree)
946 || (tree == TREE_NULL))
947 return;
948
949 if (method == 1)
950 {
951 process(tree, pUser);
952 treeTraverse (((TREE*)tree)->left, process, pUser, method);
953 treeTraverse (((TREE*)tree)->right, process, pUser, method);
954 }
955 else if (method == 2)
956 {
957 treeTraverse (((TREE*)tree)->left, process, pUser, method);
958 treeTraverse (((TREE*)tree)->right, process, pUser, method);
959 process(tree, pUser);
960 }
961 else
962 {
963 treeTraverse (((TREE*)tree)->left, process, pUser, method);
964 process(tree, pUser);
965 treeTraverse (((TREE*)tree)->right, process, pUser, method);
966 }
967}
968
969/*
970 *@@ treeFirst:
971 * finds and returns the first node in a (sub-)tree.
972 *
973 * See treeNext for a sample usage for traversing a tree.
974 */
975
976void* treeFirst(TREE *tree)
977{
978 TREE
979 *current;
980
981 if ( (!tree)
982 || (tree == TREE_NULL)
983 )
984 return NULL;
985
986 current = tree;
987 while (current->left != TREE_NULL)
988 current = current->left;
989
990 return current;
991}
992
993/*
994 *@@ treeLast:
995 * finds and returns the last node in a (sub-)tree.
996 */
997
998void* treeLast(TREE *tree)
999{
1000 TREE
1001 *current;
1002
1003 if ( (!tree)
1004 || (tree == TREE_NULL))
1005 return NULL;
1006
1007 current = tree;
1008 while (current->right != TREE_NULL)
1009 current = current->right;
1010
1011 return current;
1012}
1013
1014/*
1015 *@@ treeNext:
1016 * finds and returns the next node in a tree.
1017 *
1018 * Example for traversing a whole tree if you don't
1019 * want to use treeTraverse:
1020 *
1021 + TREE *TreeRoot;
1022 + ...
1023 + TREE* pNode = treeFirst(TreeRoot);
1024 + while (pNode)
1025 + {
1026 + ...
1027 + pNode = treeNext(pNode);
1028 + }
1029 *
1030 * This runs through the tree items in sorted order.
1031 */
1032
1033void* treeNext(TREE *tree)
1034{
1035 TREE
1036 *current,
1037 *child;
1038
1039 if ( (!tree)
1040 || (tree == TREE_NULL)
1041 )
1042 return NULL;
1043
1044 current = tree;
1045 if (current->right != TREE_NULL)
1046 return treeFirst (current->right);
1047 else
1048 {
1049 current = tree;
1050 child = TREE_NULL;
1051 while ( (current->parent)
1052 && (current->right == child)
1053 )
1054 {
1055 child = current;
1056 current = current->parent;
1057 }
1058 if (current->right != child)
1059 return current;
1060 else
1061 return NULL;
1062 }
1063}
1064
1065/*
1066 *@@ treePrev:
1067 * finds and returns the previous node in a tree.
1068 */
1069
1070void* treePrev(TREE *tree)
1071{
1072 TREE
1073 *current,
1074 *child;
1075
1076 if ( (!tree)
1077 || (tree == TREE_NULL))
1078 return NULL;
1079
1080 current = tree;
1081 if (current->left != TREE_NULL)
1082 return treeLast (current->left);
1083 else
1084 {
1085 current = tree;
1086 child = TREE_NULL;
1087 while ((current->parent)
1088 && (current->left == child))
1089 {
1090 child = current;
1091 current = current->parent;
1092 }
1093 if (current->left != child)
1094 return current;
1095 else
1096 return NULL;
1097 }
1098}
1099
1100/*
1101 *@@ treeBuildArray:
1102 * builds an array of TREE* pointers containing
1103 * all tree items in sorted order.
1104 *
1105 * This returns a TREE** pointer to the array.
1106 * Each item in the array is a TREE* pointer to
1107 * the respective tree item.
1108 *
1109 * The array has been allocated using malloc()
1110 * and must be free()'d by the caller.
1111 *
1112 * NOTE: This will only work if you maintain a
1113 * tree node count yourself, which you must pass
1114 * in *pulCount on input.
1115 *
1116 * This is most useful if you want to delete an
1117 * entire tree without having to traverse it
1118 * and rebalance the tree on every delete.
1119 *
1120 * Example usage for deletion:
1121 *
1122 + TREE *G_TreeRoot;
1123 + treeInit(&G_TreeRoot);
1124 +
1125 + // add stuff to the tree
1126 + TREE *pNewNode = malloc(...);
1127 + treeInsertID(&G_TreeRoot, pNewNode, FALSE)
1128 +
1129 + // now delete all nodes
1130 + ULONG cItems = ... // insert item count here
1131 + TREE** papNodes = treeBuildArray(G_TreeRoot,
1132 + &cItems);
1133 + if (papNodes)
1134 + {
1135 + ULONG ul;
1136 + for (ul = 0; ul < cItems; ul++)
1137 + {
1138 + TREE *pNodeThis = papNodes[ul];
1139 + free(pNodeThis);
1140 + }
1141 +
1142 + free(papNodes);
1143 + }
1144 +
1145 *
1146 *@@added V0.9.9 (2001-04-05) [umoeller]
1147 */
1148
1149TREE** treeBuildArray(TREE* pRoot,
1150 unsigned long *pulCount) // in: item count, out: array item count
1151{
1152 TREE **papNodes = NULL,
1153 **papThis = NULL;
1154 unsigned long cb = (sizeof(TREE*) * (*pulCount)),
1155 cNodes = 0;
1156
1157 if (cb)
1158 {
1159 papNodes = (TREE**)malloc(cb);
1160 papThis = papNodes;
1161
1162 if (papNodes)
1163 {
1164 TREE *pNode = (TREE*)treeFirst(pRoot);
1165
1166 memset(papNodes, 0, cb);
1167
1168 // copy nodes to array
1169 while ( pNode
1170 && cNodes < (*pulCount) // just to make sure
1171 )
1172 {
1173 *papThis = pNode;
1174 cNodes++;
1175 papThis++;
1176
1177 pNode = (TREE*)treeNext(pNode);
1178 }
1179
1180 // output count
1181 *pulCount = cNodes;
1182 }
1183 }
1184
1185 return (papNodes);
1186}
1187
1188
Note: See TracBrowser for help on using the repository browser.