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

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

More updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 32.0 KB
Line 
1
2/*
3 *@@sourcefile tree.c:
4 * contains helper functions for maintaining 'Red-Black' balanced
5 * binary trees.
6 *
7 * Usage: All C programs; not OS/2-specific.
8 *
9 * Function prefixes (new with V0.81):
10 * -- tree* tree helper functions
11 *
12 * <B>Introduction</B>
13 *
14 * While linked lists have "next" and "previous" pointers (which
15 * makes them one-dimensional), trees have a two-dimensional layout:
16 * each tree node has one "parent" and two "children" which are
17 * called "left" and "right". The "left" pointer will always lead
18 * to a tree node that is "less than" its parent node, while the
19 * "right" pointer will lead to a node that is "greater than"
20 * its parent. What is considered "less" or "greater" for sorting
21 * is determined by a comparison callback to be supplied by the
22 * tree functions' caller. The "leafs" of the tree will have
23 * null left and right pointers.
24 *
25 * For this, the functions here use the TREE structure. The most
26 * important member here is the "ulKey" field which is used for
27 * sorting (passed to the compare callbacks). Since the tree
28 * functions do no memory allocation, the caller can easily
29 * use an extended TREE structure with additional fields as
30 * long as the first member is the TREE structure. See below.
31 *
32 * Each tree must have a "root" item, from which all other tree
33 * nodes can eventually be reached by following the "left" and
34 * "right" pointers. The root node is the only node whose
35 * parent is null.
36 *
37 * <B>Trees vs. linked lists</B>
38 *
39 * Compared to linked lists (as implemented by linklist.c),
40 * trees allow for much faster searching, since they are
41 * always sorted.
42 *
43 * Take an array of numbers, and assume you'd need to find
44 * the array node with the specified number.
45 *
46 * With a (sorted) linked list, this would look like:
47 *
48 + 4 --> 7 --> 16 --> 20 --> 37 --> 38 --> 43
49 *
50 * Searching for "43" would need 6 iterations.
51 *
52 * With a binary tree, this would instead look like:
53 *
54 + 20
55 + / \
56 + 7 38
57 + / \ / \
58 + 4 16 37 43
59 + / \ / \ / \ / \
60 *
61 * Searching for "43" would need 2 iterations only.
62 *
63 * Assuming a linked list contains N items, then searching a
64 * linked list for an item will take an average of N/2 comparisons
65 * and even N comparisons if the item cannot be found (unless
66 * you keep the list sorted, but linklist.c doesn't do this).
67 *
68 * According to "Algorithms in C", a search in a balanced
69 * "red-black" binary tree takes about lg N comparisons on
70 * average, and insertions take less than one rotation on
71 * average.
72 *
73 * Differences compared to linklist.c:
74 *
75 * -- A tree is always sorted.
76 *
77 * -- Trees are considerably slower when inserting and removing
78 * nodes because the tree has to be rebalanced every time
79 * a node changes. By contrast, trees are much faster finding
80 * nodes because the tree is always sorted.
81 *
82 * -- As opposed to a LISTNODE, the TREE structure (which
83 * represents a tree node) does not contain a data pointer,
84 * as said above. The caller must do all memory management.
85 *
86 * <B>Background</B>
87 *
88 * Now, a "red-black balanced binary tree" means the following:
89 *
90 * -- We have "binary" trees. That is, there are only "left" and
91 * "right" pointers. (Other algorithms allow tree nodes to
92 * have more than two children, but binary trees are usually
93 * more efficient.)
94 *
95 * -- The tree is always "balanced". The tree gets reordered
96 * when items are added/removed to ensure that all paths
97 * through the tree are approximately the same length.
98 * This avoids the "worst case" scenario that some paths
99 * grow terribly long while others remain short ("degenerated"
100 * trees), which can make searching very inefficient:
101 *
102 + 4
103 + / \
104 + 7
105 + / \
106 + 16
107 + / \
108 + 20
109 + / \
110 + 37
111 + / \
112 + 43
113 + / \
114 *
115 * -- Fully balanced trees can be quite expensive because on
116 * every insertion or deletion, the tree nodes must be rotated.
117 * By contrast, "Red-black" binary balanced trees contain
118 * an additional bit in each node which marks the node as
119 * either red or black. This bit is used only for efficient
120 * rebalancing when inserting or deleting nodes.
121 *
122 * I don't fully understand why this works, but if you really
123 * care, this is explained at
124 * "http://www.eli.sdsu.edu/courses/fall96/cs660/notes/redBlack/redBlack.html".
125 *
126 * In other words, as opposed to regular binary trees, RB trees
127 * are not _fully_ balanced, but they are _mostly_ balanced. With
128 * respect to efficiency, RB trees are thus a good compromise:
129 *
130 * -- Completely unbalanced trees are efficient when inserting,
131 * but can have a terrible worst case when searching.
132 *
133 * -- RB trees are still acceptably efficient when inserting
134 * and quite efficient when searching.
135 * A RB tree with n internal nodes has a height of at most
136 * 2lg(n+1). Both average and worst-case search time is O(lg n).
137 *
138 * -- Fully balanced binary trees are inefficient when inserting
139 * but most efficient when searching.
140 *
141 * So as long as you are sure that trees are more efficient
142 * in your situation than a linked list in the first place, use
143 * these RB trees instead of linked lists.
144 *
145 * <B>Using binary trees</B>
146 *
147 * You can use any structure as elements in a tree, provided
148 * that the first member in the structure is a TREE structure
149 * (i.e. it has the left, right, parent, and color members).
150 * Each TREE node has a ulKey field which is used for
151 * comparing tree nodes and thus determines the location of
152 * the node in the tree.
153 *
154 * The tree functions don't care what follows in each TREE
155 * node since they do not manage any memory themselves.
156 * So the implementation here is slightly different from the
157 * linked lists in linklist.c, because the LISTNODE structs
158 * only have pointers to the data. By contrast, the TREE structs
159 * are expected to contain the data themselves.
160 *
161 * Initialize the root of the tree with treeInit(). Then
162 * add nodes to the tree with treeInsert() and remove nodes
163 * with treeDelete(). See below for a sample.
164 *
165 * You can test whether a tree is empty by comparing its
166 * root with LEAF.
167 *
168 * For most tree* functions, you must specify a comparison
169 * function which will always receive two "key" parameters
170 * to compare. This must be declared as
171 +
172 + int TREEENTRY fnCompare(ULONG ul1, ULONG ul2);
173 *
174 * This will receive two TREE.ulKey members (whose meaning
175 * is defined by your implementation) and must return
176 *
177 * -- something < 0: ul1 < ul2
178 * -- 0: ul1 == ul2
179 * -- something > 1: ul1 > ul2
180 *
181 * <B>Example</B>
182 *
183 * A good example where trees are efficient would be the
184 * case where you have "keyword=value" string pairs and
185 * you frequently need to search for "keyword" to find
186 * a "value". So "keyword" would be an ideal candidate for
187 * the TREE.key field.
188 *
189 * You'd then define your own tree nodes like this:
190 *
191 + typedef struct _MYTREENODE
192 + {
193 + TREE Tree; // regular tree node, which has
194 + // the ULONG "key" field; we'd
195 + // use this as a const char*
196 + // pointer to the keyword string
197 + // here come the additional fields
198 + // (whatever you need for your data)
199 + const char *pcszValue;
200 +
201 + } MYTREENODE, *PMYTREENODE;
202 *
203 * Initialize the tree root:
204 *
205 + TREE *root;
206 + treeInit(&root);
207 *
208 * To add a new "keyword=value" pair, do this:
209 *
210 + PMYTREENODE AddNode(TREE **root,
211 + const char *pcszKeyword,
212 + const char *pcszValue)
213 + {
214 + PMYTREENODE p = (PMYTREENODE)malloc(sizeof(MYTREENODE));
215 + p.Tree.ulKey = (ULONG)pcszKeyword;
216 + p.pcszValue = pcszValue;
217 + treeInsert(root, // tree's root
218 + p, // new tree node
219 + fnCompare); // comparison func
220 + return (p);
221 + }
222 *
223 * Your comparison func receives two ulKey values to compare,
224 * which in this case would be the typecast string pointers:
225 *
226 + int TREEENTRY fnCompare(ULONG ul1, ULONG ul2)
227 + {
228 + return (strcmp((const char*)ul1,
229 + (const char*)ul2));
230 + }
231 *
232 * You can then use treeFind to very quickly find a node
233 * with a specified ulKey member.
234 *
235 * This file was new with V0.9.5 (2000-09-29) [umoeller].
236 * With V0.9.13, all the code has been replaced with the public
237 * domain code found at http://epaperpress.com/sortsearch/index.html
238 * ("A compact guide to searching and sorting") by Thomas Niemann.
239 * The old implementation from the Standard Function Library (SFL)
240 * turned out to be buggy for large trees (more than 100 nodes).
241 *
242 *@@added V0.9.5 (2000-09-29) [umoeller]
243 *@@header "helpers\tree.h"
244 */
245
246/*
247 * Original coding by Thomas Niemann, placed in the public domain
248 * (see http://epaperpress.com/sortsearch/index.html).
249 *
250 * This implementation Copyright (C) 2001 Ulrich M”ller.
251 * This file is part of the "XWorkplace helpers" source package.
252 * This is free software; you can redistribute it and/or modify
253 * it under the terms of the GNU General Public License as published
254 * by the Free Software Foundation, in version 2 as it comes in the
255 * "COPYING" file of the XWorkplace main distribution.
256 * This program is distributed in the hope that it will be useful,
257 * but WITHOUT ANY WARRANTY; without even the implied warranty of
258 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
259 * GNU General Public License for more details.
260 */
261
262/*
263 *@@category: Helpers\C helpers\Red-black balanced binary trees
264 * See tree.c.
265 */
266
267#include "setup.h"
268#include "helpers\tree.h"
269
270#define LEAF &sentinel // all leafs are sentinels
271static TREE sentinel = { LEAF, LEAF, 0, BLACK};
272
273/*
274A binary search tree is a red-black tree if:
275
2761. Every node is either red or black.
2772. Every leaf (nil) is black.
2783. If a node is red, then both its children are black.
2794. Every simple path from a node to a descendant leaf contains the same
280 number of black nodes.
281*/
282
283/*
284 *@@ treeInit:
285 * initializes the root of a tree.
286 *
287 * If (plCount != NULL), *plCount is set to null also.
288 * This same plCount pointer can then be passed to
289 * treeInsert and treeDelete also to automatically
290 * maintain a tree item count.
291 *
292 *@@changed V0.9.16 (2001-10-19) [umoeller]: added plCount
293 */
294
295void treeInit(TREE **root,
296 PLONG plCount) // out: tree item count, set to 0 (ptr can be NULL)
297{
298 *root = LEAF;
299
300 if (plCount)
301 *plCount = 0; // V0.9.16 (2001-10-19) [umoeller]
302}
303
304/*
305 *@@ treeCompareKeys:
306 * standard comparison func if the TREE.ulKey
307 * field really is a ULONG.
308 */
309
310int TREEENTRY treeCompareKeys(unsigned long ul1, unsigned long ul2)
311{
312 if (ul1 < ul2)
313 return -1;
314 if (ul1 > ul2)
315 return +1;
316 return (0);
317}
318
319/*
320 *@@ treeCompareStrings:
321 * standard comparison func if the TREE.ulKey
322 * field really is a string pointer (PCSZ).
323 *
324 * This runs strcmp internally, but can handle
325 * NULL pointers without crashing.
326 *
327 *@@added V0.9.16 (2001-09-29) [umoeller]
328 */
329
330int TREEENTRY treeCompareStrings(unsigned long ul1, unsigned long ul2)
331{
332 #define p1 (const char*)(ul1)
333 #define p2 (const char*)(ul2)
334
335 if (p1 && p2)
336 {
337 int i = strcmp(p1, p2);
338 if (i < 0) return (-1);
339 if (i > 0) return (+1);
340 }
341 else if (p1)
342 // but p2 is NULL: p1 greater than p2 then
343 return (+1);
344 else if (p2)
345 // but p1 is NULL: p1 less than p2 then
346 return (-1);
347
348 // return 0 if strcmp returned 0 above or both strings are NULL
349 return (0);
350}
351
352/*
353 *@@ rotateLeft:
354 * private function during rebalancing.
355 */
356
357static void rotateLeft(TREE **root,
358 TREE *x)
359{
360 /**************************
361 * rotate node x to left *
362 **************************/
363
364 TREE *y = x->right;
365
366 // establish x->right link
367 x->right = y->left;
368 if (y->left != LEAF)
369 y->left->parent = x;
370
371 // establish y->parent link
372 if (y != LEAF)
373 y->parent = x->parent;
374 if (x->parent)
375 {
376 if (x == x->parent->left)
377 x->parent->left = y;
378 else
379 x->parent->right = y;
380 }
381 else
382 *root = y;
383
384 // link x and y
385 y->left = x;
386 if (x != LEAF)
387 x->parent = y;
388}
389
390/*
391 *@@ rotateRight:
392 * private function during rebalancing.
393 */
394
395static void rotateRight(TREE **root,
396 TREE *x)
397{
398
399 /****************************
400 * rotate node x to right *
401 ****************************/
402
403 TREE *y = x->left;
404
405 // establish x->left link
406 x->left = y->right;
407 if (y->right != LEAF)
408 y->right->parent = x;
409
410 // establish y->parent link
411 if (y != LEAF)
412 y->parent = x->parent;
413 if (x->parent)
414 {
415 if (x == x->parent->right)
416 x->parent->right = y;
417 else
418 x->parent->left = y;
419 }
420 else
421 *root = y;
422
423 // link x and y
424 y->right = x;
425 if (x != LEAF)
426 x->parent = y;
427}
428
429/*
430 *@@ insertFixup:
431 * private function during rebalancing.
432 */
433
434static void insertFixup(TREE **root,
435 TREE *x)
436{
437 /*************************************
438 * maintain Red-Black tree balance *
439 * after inserting node x *
440 *************************************/
441
442 // check Red-Black properties
443 while ( x != *root
444 && x->parent->color == RED
445 )
446 {
447 // we have a violation
448 if (x->parent == x->parent->parent->left)
449 {
450 TREE *y = x->parent->parent->right;
451 if (y->color == RED)
452 {
453 // uncle is RED
454 x->parent->color = BLACK;
455 y->color = BLACK;
456 x->parent->parent->color = RED;
457 x = x->parent->parent;
458 }
459 else
460 {
461 // uncle is BLACK
462 if (x == x->parent->right)
463 {
464 // make x a left child
465 x = x->parent;
466 rotateLeft(root,
467 x);
468 }
469
470 // recolor and rotate
471 x->parent->color = BLACK;
472 x->parent->parent->color = RED;
473 rotateRight(root,
474 x->parent->parent);
475 }
476 }
477 else
478 {
479 // mirror image of above code
480 TREE *y = x->parent->parent->left;
481 if (y->color == RED)
482 {
483 // uncle is RED
484 x->parent->color = BLACK;
485 y->color = BLACK;
486 x->parent->parent->color = RED;
487 x = x->parent->parent;
488 }
489 else
490 {
491 // uncle is BLACK
492 if (x == x->parent->left)
493 {
494 x = x->parent;
495 rotateRight(root,
496 x);
497 }
498 x->parent->color = BLACK;
499 x->parent->parent->color = RED;
500 rotateLeft(root,
501 x->parent->parent);
502 }
503 }
504 }
505 (*root)->color = BLACK;
506}
507
508/*
509 *@@ treeInsert:
510 * inserts a new tree node into the specified
511 * tree, using the specified comparison function
512 * for sorting.
513 *
514 * "x" specifies the new tree node which must
515 * have been allocated by the caller. x->ulKey
516 * must already contain the node's key (data)
517 * which the sort function can understand.
518 *
519 * This function will then set the parent,
520 * left, right, and color members. In addition,
521 * if (plCount != NULL), *plCount is raised by
522 * one.
523 *
524 * Returns 0 if no error. Might return
525 * STATUS_DUPLICATE_KEY if a node with the
526 * same ulKey already exists.
527 *
528 *@@changed V0.9.16 (2001-10-19) [umoeller]: added plCount
529 */
530
531int treeInsert(TREE **root, // in: root of the tree
532 PLONG plCount, // in/out: item count (ptr can be NULL)
533 TREE *x, // in: new node to insert
534 FNTREE_COMPARE *pfnCompare) // in: comparison func
535{
536 TREE *current,
537 *parent;
538
539 unsigned long key = x->ulKey;
540
541 // find future parent
542 current = *root;
543 parent = 0;
544
545 while (current != LEAF)
546 {
547 int iResult;
548 if (0 == (iResult = pfnCompare(key, current->ulKey))) // if (compEQ(key, current->key))
549 return STATUS_DUPLICATE_KEY;
550
551 parent = current;
552 current = (iResult < 0) // compLT(key, current->key)
553 ? current->left
554 : current->right;
555 }
556
557 // set up new node
558 /* if ((x = malloc (sizeof(*x))) == 0)
559 return STATUS_MEM_EXHAUSTED; */
560 x->parent = parent;
561 x->left = LEAF;
562 x->right = LEAF;
563 x->color = RED;
564 // x->key = key;
565 // x->rec = *rec;
566
567 // insert node in tree
568 if (parent)
569 {
570 if (pfnCompare(key, parent->ulKey) < 0) // (compLT(key, parent->key))
571 parent->left = x;
572 else
573 parent->right = x;
574 }
575 else
576 *root = x;
577
578 insertFixup(root,
579 x);
580 // lastFind = NULL;
581
582 if (plCount)
583 (*plCount)++; // V0.9.16 (2001-10-19) [umoeller]
584
585 return STATUS_OK;
586}
587
588/*
589 *@@ deleteFixup:
590 *
591 */
592
593static void deleteFixup(TREE **root,
594 TREE *tree)
595{
596 TREE *s;
597
598 while ( tree != *root
599 && tree->color == BLACK
600 )
601 {
602 if (tree == tree->parent->left)
603 {
604 s = tree->parent->right;
605 if (s->color == RED)
606 {
607 s->color = BLACK;
608 tree->parent->color = RED;
609 rotateLeft(root, tree->parent);
610 s = tree->parent->right;
611 }
612 if ( (s->left->color == BLACK)
613 && (s->right->color == BLACK)
614 )
615 {
616 s->color = RED;
617 tree = tree->parent;
618 }
619 else
620 {
621 if (s->right->color == BLACK)
622 {
623 s->left->color = BLACK;
624 s->color = RED;
625 rotateRight(root, s);
626 s = tree->parent->right;
627 }
628 s->color = tree->parent->color;
629 tree->parent->color = BLACK;
630 s->right->color = BLACK;
631 rotateLeft(root, tree->parent);
632 tree = *root;
633 }
634 }
635 else
636 {
637 s = tree->parent->left;
638 if (s->color == RED)
639 {
640 s->color = BLACK;
641 tree->parent->color = RED;
642 rotateRight(root, tree->parent);
643 s = tree->parent->left;
644 }
645 if ( (s->right->color == BLACK)
646 && (s->left->color == BLACK)
647 )
648 {
649 s->color = RED;
650 tree = tree->parent;
651 }
652 else
653 {
654 if (s->left->color == BLACK)
655 {
656 s->right->color = BLACK;
657 s->color = RED;
658 rotateLeft(root, s);
659 s = tree->parent->left;
660 }
661 s->color = tree->parent->color;
662 tree->parent->color = BLACK;
663 s->left->color = BLACK;
664 rotateRight (root, tree->parent);
665 tree = *root;
666 }
667 }
668 }
669 tree->color = BLACK;
670
671 /*************************************
672 * maintain Red-Black tree balance *
673 * after deleting node x *
674 *************************************/
675
676 /* while ( x != *root
677 && x->color == BLACK
678 )
679 {
680 if (x == x->parent->left)
681 {
682 TREE *w = x->parent->right;
683 if (w->color == RED)
684 {
685 w->color = BLACK;
686 x->parent->color = RED;
687 rotateLeft(root,
688 x->parent);
689 w = x->parent->right;
690 }
691 if ( w->left->color == BLACK
692 && w->right->color == BLACK
693 )
694 {
695 w->color = RED;
696 x = x->parent;
697 }
698 else
699 {
700 if (w->right->color == BLACK)
701 {
702 w->left->color = BLACK;
703 w->color = RED;
704 rotateRight(root,
705 w);
706 w = x->parent->right;
707 }
708 w->color = x->parent->color;
709 x->parent->color = BLACK;
710 w->right->color = BLACK;
711 rotateLeft(root,
712 x->parent);
713 x = *root;
714 }
715 }
716 else
717 {
718 TREE *w = x->parent->left;
719 if (w->color == RED)
720 {
721 w->color = BLACK;
722 x->parent->color = RED;
723 rotateRight(root,
724 x->parent);
725 w = x->parent->left;
726 }
727 if ( w->right->color == BLACK
728 && w->left->color == BLACK
729 )
730 {
731 w->color = RED;
732 x = x->parent;
733 }
734 else
735 {
736 if (w->left->color == BLACK)
737 {
738 w->right->color = BLACK;
739 w->color = RED;
740 rotateLeft(root,
741 w);
742 w = x->parent->left;
743 }
744 w->color = x->parent->color;
745 x->parent->color = BLACK;
746 w->left->color = BLACK;
747 rotateRight(root,
748 x->parent);
749 x = *root;
750 }
751 }
752 }
753 x->color = BLACK; */
754}
755
756/*
757 *@@ treeDelete:
758 * removes the specified node from the tree.
759 * Does not free() the node though.
760 *
761 * In addition, if (plCount != NULL), *plCount is
762 * decremented.
763 *
764 * Returns 0 if the node was deleted or
765 * STATUS_INVALID_NODE if not.
766 *
767 *@@changed V0.9.16 (2001-10-19) [umoeller]: added plCount
768 */
769
770int treeDelete(TREE **root, // in: root of the tree
771 PLONG plCount, // in/out: item count (ptr can be NULL)
772 TREE *tree) // in: tree node to delete
773{
774 TREE *y,
775 *d;
776 nodeColor color;
777
778 if ( (!tree)
779 || (tree == LEAF)
780 )
781 return STATUS_INVALID_NODE;
782
783 if ( (tree->left == LEAF)
784 || (tree->right == LEAF)
785 )
786 // d has a TREE_NULL node as a child
787 d = tree;
788 else
789 {
790 // find tree successor with a TREE_NULL node as a child
791 d = tree->right;
792 while (d->left != LEAF)
793 d = d->left;
794 }
795
796 // y is d's only child, if there is one, else TREE_NULL
797 if (d->left != LEAF)
798 y = d->left;
799 else
800 y = d->right;
801
802 // remove d from the parent chain
803 if (y != LEAF)
804 y->parent = d->parent;
805 if (d->parent)
806 {
807 if (d == d->parent->left)
808 d->parent->left = y;
809 else
810 d->parent->right = y;
811 }
812 else
813 *root = y;
814
815 color = d->color;
816
817 if (d != tree)
818 {
819 // move the data from d to tree; we do this by
820 // linking d into the structure in the place of tree
821 d->left = tree->left;
822 d->right = tree->right;
823 d->parent = tree->parent;
824 d->color = tree->color;
825
826 if (d->parent)
827 {
828 if (tree == d->parent->left)
829 d->parent->left = d;
830 else
831 d->parent->right = d;
832 }
833 else
834 *root = d;
835
836 if (d->left != LEAF)
837 d->left->parent = d;
838
839 if (d->right != LEAF)
840 d->right->parent = d;
841 }
842
843 if ( (y != LEAF)
844 && (color == BLACK)
845 )
846 deleteFixup(root,
847 y);
848
849 if (plCount)
850 (*plCount)--; // V0.9.16 (2001-10-19) [umoeller]
851
852 return (STATUS_OK);
853}
854
855/*
856 *@@ treeFind:
857 * finds the tree node with the specified key.
858 * Returns NULL if none exists.
859 */
860
861TREE* treeFind(TREE *root, // in: root of the tree
862 unsigned long key, // in: key to find
863 FNTREE_COMPARE *pfnCompare) // in: comparison func
864{
865 /*******************************
866 * find node containing data *
867 *******************************/
868
869 TREE *current = root;
870 while (current != LEAF)
871 {
872 int iResult;
873 if (0 == (iResult = pfnCompare(key, current->ulKey)))
874 return (current);
875 else
876 {
877 current = (iResult < 0) // compLT (key, current->key)
878 ? current->left
879 : current->right;
880 }
881 }
882
883 return 0;
884}
885
886/*
887 *@@ treeFirst:
888 * finds and returns the first node in a (sub-)tree.
889 *
890 * See treeNext for a sample usage for traversing a tree.
891 */
892
893TREE* treeFirst(TREE *r)
894{
895 TREE *p;
896
897 if ( (!r)
898 || (r == LEAF)
899 )
900 return NULL;
901
902 p = r;
903 while (p->left != LEAF)
904 p = p->left;
905
906 return p;
907}
908
909/*
910 *@@ treeLast:
911 * finds and returns the last node in a (sub-)tree.
912 */
913
914TREE* treeLast(TREE *r)
915{
916 TREE *p;
917
918 if ( (!r)
919 || (r == LEAF))
920 return NULL;
921
922 p = r;
923 while (p->right != LEAF)
924 p = p->right;
925
926 return p;
927}
928
929/*
930 *@@ treeNext:
931 * finds and returns the next node in a tree.
932 *
933 * Example for traversing a whole tree:
934 *
935 + TREE *TreeRoot;
936 + ...
937 + TREE* pNode = treeFirst(TreeRoot);
938 + while (pNode)
939 + {
940 + ...
941 + pNode = treeNext(pNode);
942 + }
943 *
944 * This runs through the tree items in sorted order.
945 */
946
947TREE* treeNext(TREE *r)
948{
949 TREE *p,
950 *child;
951
952 if ( (!r)
953 || (r == LEAF)
954 )
955 return NULL;
956
957 p = r;
958 if (p->right != LEAF)
959 return treeFirst (p->right);
960 else
961 {
962 p = r;
963 child = LEAF;
964 while ( (p->parent)
965 && (p->right == child)
966 )
967 {
968 child = p;
969 p = p->parent;
970 }
971 if (p->right != child)
972 return p;
973 else
974 return NULL;
975 }
976}
977
978/*
979 *@@ treePrev:
980 * finds and returns the previous node in a tree.
981 */
982
983TREE* treePrev(TREE *r)
984{
985 TREE *p,
986 *child;
987
988 if ( (!r)
989 || (r == LEAF))
990 return NULL;
991
992 p = r;
993 if (p->left != LEAF)
994 return treeLast (p->left);
995 else
996 {
997 p = r;
998 child = LEAF;
999 while ((p->parent)
1000 && (p->left == child))
1001 {
1002 child = p;
1003 p = p->parent;
1004 }
1005 if (p->left != child)
1006 return p;
1007 else
1008 return NULL;
1009 }
1010}
1011
1012/*
1013 *@@ treeBuildArray:
1014 * builds an array of TREE* pointers containing
1015 * all tree items in sorted order.
1016 *
1017 * This returns a TREE** pointer to the array.
1018 * Each item in the array is a TREE* pointer to
1019 * the respective tree item.
1020 *
1021 * The array has been allocated using malloc()
1022 * and must be free()'d by the caller.
1023 *
1024 * NOTE: This will only work if you maintain a
1025 * tree node count yourself, which you must pass
1026 * in *pulCount on input.
1027 *
1028 * This is most useful if you want to delete an
1029 * entire tree without having to traverse it
1030 * and rebalance the tree on every delete.
1031 *
1032 * Example usage for deletion:
1033 *
1034 + TREE *G_TreeRoot;
1035 + treeInit(&G_TreeRoot);
1036 +
1037 + // add stuff to the tree
1038 + TREE *pNewNode = malloc(...);
1039 + treeInsert(&G_TreeRoot, pNewNode, fnCompare)
1040 +
1041 + // now delete all nodes
1042 + ULONG cItems = ... // insert item count here
1043 + TREE** papNodes = treeBuildArray(G_TreeRoot,
1044 + &cItems);
1045 + if (papNodes)
1046 + {
1047 + ULONG ul;
1048 + for (ul = 0; ul < cItems; ul++)
1049 + {
1050 + TREE *pNodeThis = papNodes[ul];
1051 + free(pNodeThis);
1052 + }
1053 +
1054 + free(papNodes);
1055 + }
1056 +
1057 *
1058 *@@added V0.9.9 (2001-04-05) [umoeller]
1059 */
1060
1061TREE** treeBuildArray(TREE* pRoot,
1062 PLONG plCount) // in: item count, out: array item count
1063{
1064 TREE **papNodes = NULL,
1065 **papThis = NULL;
1066 long cb = (sizeof(TREE*) * (*plCount)),
1067 cNodes = 0;
1068
1069 if (cb)
1070 {
1071 papNodes = (TREE**)malloc(cb);
1072 papThis = papNodes;
1073
1074 if (papNodes)
1075 {
1076 TREE *pNode = (TREE*)treeFirst(pRoot);
1077
1078 memset(papNodes, 0, cb);
1079
1080 // copy nodes to array
1081 while ( pNode
1082 && cNodes < (*plCount) // just to make sure
1083 )
1084 {
1085 *papThis = pNode;
1086 cNodes++;
1087 papThis++;
1088
1089 pNode = (TREE*)treeNext(pNode);
1090 }
1091
1092 // output count
1093 *plCount = cNodes;
1094 }
1095 }
1096
1097 return (papNodes);
1098}
1099
1100/* void main(int argc, char **argv) {
1101 int maxnum, ct;
1102 recType rec;
1103 keyType key;
1104 statusEnum status;
1105
1106 maxnum = atoi(argv[1]);
1107
1108 printf("maxnum = %d\n", maxnum);
1109 for (ct = maxnum; ct; ct--) {
1110 key = rand() % 9 + 1;
1111 if ((status = find(key, &rec)) == STATUS_OK) {
1112 status = delete(key);
1113 if (status) printf("fail: status = %d\n", status);
1114 } else {
1115 status = insert(key, &rec);
1116 if (status) printf("fail: status = %d\n", status);
1117 }
1118 }
1119} */
Note: See TracBrowser for help on using the repository browser.