source: trunk/src/helpers/tree.c

Last change on this file was 388, checked in by pr, 15 years ago

Replace #define with variable.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 28.7 KB
RevLine 
[8]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 *
[33]12 * <B>Introduction</B>
13 *
[83]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.
[33]24 *
[83]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.
[56]31 *
[83]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.
[56]36 *
[83]37 * <B>Trees vs. linked lists</B>
[56]38 *
[83]39 * Compared to linked lists (as implemented by linklist.c),
40 * trees allow for much faster searching, since they are
41 * always sorted.
[33]42 *
[83]43 * Take an array of numbers, and assume you'd need to find
44 * the array node with the specified number.
[33]45 *
[83]46 * With a (sorted) linked list, this would look like:
[33]47 *
[83]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 *
[33]90 * -- We have "binary" trees. That is, there are only "left" and
[83]91 * "right" pointers. (Other algorithms allow tree nodes to
92 * have more than two children, but binary trees are usually
93 * more efficient.)
[33]94 *
[83]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:
[33]101 *
[83]102 + 4
103 + / \
104 + 7
105 + / \
106 + 16
107 + / \
108 + 20
109 + / \
110 + 37
111 + / \
112 + 43
113 + / \
[33]114 *
[83]115 * -- Fully balanced trees can be quite expensive because on
[85]116 * every insertion or deletion, the tree nodes must be rotated.
[83]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.
[33]121 *
[83]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 *
[8]145 * <B>Using binary trees</B>
146 *
[13]147 * You can use any structure as elements in a tree, provided
148 * that the first member in the structure is a TREE structure
[83]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.
[13]153 *
[83]154 * The tree functions don't care what follows in each TREE
155 * node since they do not manage any memory themselves.
[13]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
[83]159 * are expected to contain the data themselves.
[13]160 *
[8]161 * Initialize the root of the tree with treeInit(). Then
[83]162 * add nodes to the tree with treeInsert() and remove nodes
163 * with treeDelete(). See below for a sample.
[8]164 *
165 * You can test whether a tree is empty by comparing its
[83]166 * root with LEAF.
[8]167 *
[83]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);
[33]173 *
[83]174 * This will receive two TREE.ulKey members (whose meaning
175 * is defined by your implementation) and must return
[33]176 *
[83]177 * -- something < 0: ul1 < ul2
178 * -- 0: ul1 == ul2
179 * -- something > 1: ul1 > ul2
[33]180 *
[83]181 * <B>Example</B>
[33]182 *
[83]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.
[54]188 *
[83]189 * You'd then define your own tree nodes like this:
[54]190 *
[83]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;
[33]202 *
[83]203 * Initialize the tree root:
[33]204 *
[83]205 + TREE *root;
206 + treeInit(&root);
[8]207 *
[83]208 * To add a new "keyword=value" pair, do this:
[8]209 *
[83]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
[238]220 + return p;
[83]221 + }
[8]222 *
[83]223 * Your comparison func receives two ulKey values to compare,
224 * which in this case would be the typecast string pointers:
[8]225 *
[83]226 + int TREEENTRY fnCompare(ULONG ul1, ULONG ul2)
227 + {
[238]228 + return strcmp((const char*)ul1,
229 + (const char*)ul2);
[83]230 + }
[8]231 *
[83]232 * You can then use treeFind to very quickly find a node
233 * with a specified ulKey member.
[8]234 *
[83]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).
[8]241 *
242 *@@added V0.9.5 (2000-09-29) [umoeller]
243 *@@header "helpers\tree.h"
244 */
245
246/*
[83]247 * Original coding by Thomas Niemann, placed in the public domain
248 * (see http://epaperpress.com/sortsearch/index.html).
[8]249 *
[83]250 * This implementation Copyright (C) 2001 Ulrich M”ller.
[14]251 * This file is part of the "XWorkplace helpers" source package.
252 * This is free software; you can redistribute it and/or modify
[8]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
[21]264 * See tree.c.
[8]265 */
266
267#include "setup.h"
268#include "helpers\tree.h"
269
[83]270#define LEAF &sentinel // all leafs are sentinels
[222]271STATIC TREE sentinel = { LEAF, LEAF, 0, BLACK};
[8]272
[83]273/*
274A binary search tree is a red-black tree if:
[8]275
[83]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
[8]283/*
284 *@@ treeInit:
[83]285 * initializes the root of a tree.
[8]286 *
[116]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 *
[113]292 *@@changed V0.9.16 (2001-10-19) [umoeller]: added plCount
[8]293 */
294
[113]295void treeInit(TREE **root,
296 PLONG plCount) // out: tree item count, set to 0 (ptr can be NULL)
[8]297{
[83]298 *root = LEAF;
[113]299
300 if (plCount)
301 *plCount = 0; // V0.9.16 (2001-10-19) [umoeller]
[8]302}
303
304/*
[83]305 *@@ treeCompareKeys:
306 * standard comparison func if the TREE.ulKey
307 * field really is a ULONG.
[33]308 */
309
[83]310int TREEENTRY treeCompareKeys(unsigned long ul1, unsigned long ul2)
[33]311{
[83]312 if (ul1 < ul2)
[33]313 return -1;
[196]314
[83]315 if (ul1 > ul2)
[33]316 return +1;
[196]317
318 return 0;
[33]319}
320
321/*
[106]322 *@@ treeCompareStrings:
323 * standard comparison func if the TREE.ulKey
324 * field really is a string pointer (PCSZ).
325 *
326 * This runs strcmp internally, but can handle
327 * NULL pointers without crashing.
328 *
329 *@@added V0.9.16 (2001-09-29) [umoeller]
330 */
331
332int TREEENTRY treeCompareStrings(unsigned long ul1, unsigned long ul2)
333{
[388]334 const char *p1 = (const char*) ul1;
335 const char *p2 = (const char*) ul2;
[106]336
337 if (p1 && p2)
338 {
339 int i = strcmp(p1, p2);
[196]340 if (i < 0)
341 return -1;
342 if (i > 0)
343 return +1;
[106]344 }
345 else if (p1)
346 // but p2 is NULL: p1 greater than p2 then
[196]347 return +1;
[106]348 else if (p2)
349 // but p1 is NULL: p1 less than p2 then
[196]350 return -1;
[106]351
352 // return 0 if strcmp returned 0 above or both strings are NULL
[196]353 return 0;
[106]354}
355
356/*
[83]357 *@@ rotateLeft:
358 * private function during rebalancing.
[8]359 */
360
[222]361STATIC void rotateLeft(TREE **root,
[83]362 TREE *x)
[8]363{
[196]364 // rotate node x to left
[8]365
[83]366 TREE *y = x->right;
367
368 // establish x->right link
369 x->right = y->left;
370 if (y->left != LEAF)
371 y->left->parent = x;
372
373 // establish y->parent link
374 if (y != LEAF)
375 y->parent = x->parent;
[196]376
[83]377 if (x->parent)
[8]378 {
[83]379 if (x == x->parent->left)
380 x->parent->left = y;
381 else
382 x->parent->right = y;
[8]383 }
384 else
[83]385 *root = y;
[8]386
[83]387 // link x and y
388 y->left = x;
389 if (x != LEAF)
390 x->parent = y;
[8]391}
392
393/*
[83]394 *@@ rotateRight:
395 * private function during rebalancing.
[8]396 */
397
[222]398STATIC void rotateRight(TREE **root,
[83]399 TREE *x)
[8]400{
[196]401 // rotate node x to right
[8]402
[83]403 TREE *y = x->left;
[8]404
[83]405 // establish x->left link
406 x->left = y->right;
407 if (y->right != LEAF)
408 y->right->parent = x;
[8]409
[83]410 // establish y->parent link
411 if (y != LEAF)
412 y->parent = x->parent;
[196]413
[83]414 if (x->parent)
415 {
416 if (x == x->parent->right)
417 x->parent->right = y;
418 else
419 x->parent->left = y;
420 }
[8]421 else
[83]422 *root = y;
[8]423
[83]424 // link x and y
425 y->right = x;
426 if (x != LEAF)
427 x->parent = y;
[8]428}
429
430/*
[83]431 *@@ insertFixup:
432 * private function during rebalancing.
[8]433 */
434
[222]435STATIC void insertFixup(TREE **root,
[83]436 TREE *x)
[8]437{
[83]438 // check Red-Black properties
439 while ( x != *root
440 && x->parent->color == RED
441 )
[8]442 {
443 // we have a violation
[83]444 if (x->parent == x->parent->parent->left)
[8]445 {
[83]446 TREE *y = x->parent->parent->right;
447 if (y->color == RED)
[8]448 {
449 // uncle is RED
[83]450 x->parent->color = BLACK;
451 y->color = BLACK;
452 x->parent->parent->color = RED;
453 x = x->parent->parent;
[8]454 }
455 else
456 {
457 // uncle is BLACK
[83]458 if (x == x->parent->right)
[8]459 {
[83]460 // make x a left child
461 x = x->parent;
462 rotateLeft(root,
463 x);
[8]464 }
465
466 // recolor and rotate
[83]467 x->parent->color = BLACK;
468 x->parent->parent->color = RED;
469 rotateRight(root,
470 x->parent->parent);
[8]471 }
472 }
473 else
474 {
475 // mirror image of above code
[83]476 TREE *y = x->parent->parent->left;
477 if (y->color == RED)
[8]478 {
479 // uncle is RED
[83]480 x->parent->color = BLACK;
481 y->color = BLACK;
482 x->parent->parent->color = RED;
483 x = x->parent->parent;
[8]484 }
485 else
486 {
487 // uncle is BLACK
[83]488 if (x == x->parent->left)
[8]489 {
[83]490 x = x->parent;
491 rotateRight(root,
492 x);
[8]493 }
[83]494 x->parent->color = BLACK;
495 x->parent->parent->color = RED;
496 rotateLeft(root,
497 x->parent->parent);
[8]498 }
499 }
500 }
[196]501
[83]502 (*root)->color = BLACK;
[8]503}
504
505/*
[83]506 *@@ treeInsert:
507 * inserts a new tree node into the specified
508 * tree, using the specified comparison function
509 * for sorting.
[8]510 *
[83]511 * "x" specifies the new tree node which must
512 * have been allocated by the caller. x->ulKey
[116]513 * must already contain the node's key (data)
514 * which the sort function can understand.
515 *
[83]516 * This function will then set the parent,
[116]517 * left, right, and color members. In addition,
518 * if (plCount != NULL), *plCount is raised by
519 * one.
[8]520 *
[83]521 * Returns 0 if no error. Might return
522 * STATUS_DUPLICATE_KEY if a node with the
523 * same ulKey already exists.
[113]524 *
525 *@@changed V0.9.16 (2001-10-19) [umoeller]: added plCount
[8]526 */
527
[83]528int treeInsert(TREE **root, // in: root of the tree
[116]529 PLONG plCount, // in/out: item count (ptr can be NULL)
[83]530 TREE *x, // in: new node to insert
531 FNTREE_COMPARE *pfnCompare) // in: comparison func
[8]532{
[83]533 TREE *current,
534 *parent;
[8]535
[83]536 unsigned long key = x->ulKey;
[8]537
[83]538 // find future parent
539 current = *root;
540 parent = 0;
[8]541
[83]542 while (current != LEAF)
[8]543 {
[83]544 int iResult;
[196]545 if (!(iResult = pfnCompare(key, current->ulKey)))
[83]546 return STATUS_DUPLICATE_KEY;
[113]547
[83]548 parent = current;
[196]549 current = (iResult < 0)
[83]550 ? current->left
551 : current->right;
[8]552 }
553
[83]554 // set up new node
555 x->parent = parent;
556 x->left = LEAF;
557 x->right = LEAF;
558 x->color = RED;
[8]559
[83]560 // insert node in tree
561 if (parent)
[11]562 {
[83]563 if (pfnCompare(key, parent->ulKey) < 0) // (compLT(key, parent->key))
564 parent->left = x;
[8]565 else
[83]566 parent->right = x;
[11]567 }
[8]568 else
[83]569 *root = x;
[8]570
[83]571 insertFixup(root,
572 x);
[8]573
[113]574 if (plCount)
575 (*plCount)++; // V0.9.16 (2001-10-19) [umoeller]
576
[83]577 return STATUS_OK;
[8]578}
579
580/*
[83]581 *@@ deleteFixup:
[8]582 *
583 */
584
[222]585STATIC void deleteFixup(TREE **root,
[83]586 TREE *tree)
[8]587{
[83]588 TREE *s;
[8]589
[83]590 while ( tree != *root
591 && tree->color == BLACK
592 )
[8]593 {
594 if (tree == tree->parent->left)
595 {
[83]596 s = tree->parent->right;
597 if (s->color == RED)
[8]598 {
[83]599 s->color = BLACK;
600 tree->parent->color = RED;
601 rotateLeft(root, tree->parent);
602 s = tree->parent->right;
[8]603 }
[83]604 if ( (s->left->color == BLACK)
605 && (s->right->color == BLACK)
606 )
[8]607 {
[83]608 s->color = RED;
[8]609 tree = tree->parent;
610 }
611 else
612 {
[83]613 if (s->right->color == BLACK)
[8]614 {
[83]615 s->left->color = BLACK;
616 s->color = RED;
617 rotateRight(root, s);
618 s = tree->parent->right;
[8]619 }
[83]620 s->color = tree->parent->color;
621 tree->parent->color = BLACK;
622 s->right->color = BLACK;
623 rotateLeft(root, tree->parent);
[8]624 tree = *root;
625 }
626 }
627 else
628 {
[83]629 s = tree->parent->left;
630 if (s->color == RED)
[8]631 {
[83]632 s->color = BLACK;
633 tree->parent->color = RED;
634 rotateRight(root, tree->parent);
635 s = tree->parent->left;
[8]636 }
[83]637 if ( (s->right->color == BLACK)
638 && (s->left->color == BLACK)
639 )
[8]640 {
[83]641 s->color = RED;
[8]642 tree = tree->parent;
643 }
644 else
645 {
[83]646 if (s->left->color == BLACK)
[8]647 {
[83]648 s->right->color = BLACK;
649 s->color = RED;
650 rotateLeft(root, s);
651 s = tree->parent->left;
[8]652 }
[83]653 s->color = tree->parent->color;
654 tree->parent->color = BLACK;
655 s->left->color = BLACK;
656 rotateRight (root, tree->parent);
[8]657 tree = *root;
658 }
659 }
660 }
[196]661
[83]662 tree->color = BLACK;
[8]663}
664
665/*
[83]666 *@@ treeDelete:
667 * removes the specified node from the tree.
668 * Does not free() the node though.
669 *
[116]670 * In addition, if (plCount != NULL), *plCount is
671 * decremented.
672 *
[83]673 * Returns 0 if the node was deleted or
674 * STATUS_INVALID_NODE if not.
[113]675 *
676 *@@changed V0.9.16 (2001-10-19) [umoeller]: added plCount
[8]677 */
678
[83]679int treeDelete(TREE **root, // in: root of the tree
[116]680 PLONG plCount, // in/out: item count (ptr can be NULL)
[83]681 TREE *tree) // in: tree node to delete
[8]682{
[83]683 TREE *y,
684 *d;
685 nodeColor color;
[8]686
[83]687 if ( (!tree)
688 || (tree == LEAF)
689 )
690 return STATUS_INVALID_NODE;
[8]691
[83]692 if ( (tree->left == LEAF)
693 || (tree->right == LEAF)
694 )
695 // d has a TREE_NULL node as a child
696 d = tree;
697 else
698 {
699 // find tree successor with a TREE_NULL node as a child
700 d = tree->right;
701 while (d->left != LEAF)
702 d = d->left;
703 }
[8]704
[83]705 // y is d's only child, if there is one, else TREE_NULL
706 if (d->left != LEAF)
707 y = d->left;
708 else
709 y = d->right;
[8]710
[83]711 // remove d from the parent chain
712 if (y != LEAF)
713 y->parent = d->parent;
[196]714
[83]715 if (d->parent)
716 {
717 if (d == d->parent->left)
718 d->parent->left = y;
719 else
720 d->parent->right = y;
721 }
722 else
723 *root = y;
[8]724
[83]725 color = d->color;
726
727 if (d != tree)
728 {
729 // move the data from d to tree; we do this by
730 // linking d into the structure in the place of tree
731 d->left = tree->left;
732 d->right = tree->right;
733 d->parent = tree->parent;
734 d->color = tree->color;
735
736 if (d->parent)
[8]737 {
[83]738 if (tree == d->parent->left)
739 d->parent->left = d;
740 else
741 d->parent->right = d;
[8]742 }
[83]743 else
744 *root = d;
[8]745
[83]746 if (d->left != LEAF)
747 d->left->parent = d;
[8]748
[83]749 if (d->right != LEAF)
750 d->right->parent = d;
751 }
[8]752
[83]753 if ( (y != LEAF)
754 && (color == BLACK)
755 )
756 deleteFixup(root,
757 y);
[8]758
[113]759 if (plCount)
760 (*plCount)--; // V0.9.16 (2001-10-19) [umoeller]
761
[238]762 return STATUS_OK;
[8]763}
764
765/*
[83]766 *@@ treeFind:
767 * finds the tree node with the specified key.
[86]768 * Returns NULL if none exists.
[8]769 */
770
[83]771TREE* treeFind(TREE *root, // in: root of the tree
772 unsigned long key, // in: key to find
773 FNTREE_COMPARE *pfnCompare) // in: comparison func
[8]774{
[83]775 TREE *current = root;
776 while (current != LEAF)
[8]777 {
[83]778 int iResult;
[196]779 if (!(iResult = pfnCompare(key, current->ulKey)))
780 return current;
781
782 current = (iResult < 0)
783 ? current->left
784 : current->right;
[8]785 }
[83]786
787 return 0;
[8]788}
789
790/*
791 *@@ treeFirst:
792 * finds and returns the first node in a (sub-)tree.
[39]793 *
794 * See treeNext for a sample usage for traversing a tree.
[8]795 */
796
[83]797TREE* treeFirst(TREE *r)
[8]798{
[83]799 TREE *p;
[8]800
[83]801 if ( (!r)
802 || (r == LEAF)
[39]803 )
[8]804 return NULL;
805
[83]806 p = r;
807 while (p->left != LEAF)
808 p = p->left;
[8]809
[83]810 return p;
[8]811}
812
813/*
814 *@@ treeLast:
815 * finds and returns the last node in a (sub-)tree.
816 */
817
[83]818TREE* treeLast(TREE *r)
[8]819{
[83]820 TREE *p;
[8]821
[83]822 if ( (!r)
823 || (r == LEAF))
[8]824 return NULL;
825
[83]826 p = r;
827 while (p->right != LEAF)
828 p = p->right;
[8]829
[83]830 return p;
[8]831}
832
833/*
834 *@@ treeNext:
835 * finds and returns the next node in a tree.
[39]836 *
[83]837 * Example for traversing a whole tree:
[39]838 *
839 + TREE *TreeRoot;
840 + ...
841 + TREE* pNode = treeFirst(TreeRoot);
842 + while (pNode)
843 + {
844 + ...
845 + pNode = treeNext(pNode);
846 + }
847 *
848 * This runs through the tree items in sorted order.
[8]849 */
850
[83]851TREE* treeNext(TREE *r)
[8]852{
[83]853 TREE *p,
854 *child;
[8]855
[83]856 if ( (!r)
857 || (r == LEAF)
[39]858 )
[8]859 return NULL;
860
[83]861 p = r;
862 if (p->right != LEAF)
[196]863 return treeFirst(p->right);
864
865 p = r;
866 child = LEAF;
867 while ( (p->parent)
868 && (p->right == child)
869 )
[8]870 {
[196]871 child = p;
872 p = p->parent;
[8]873 }
[196]874
875 if (p->right != child)
876 return p;
877
878 return NULL;
[8]879}
880
881/*
882 *@@ treePrev:
883 * finds and returns the previous node in a tree.
884 */
885
[83]886TREE* treePrev(TREE *r)
[8]887{
[83]888 TREE *p,
889 *child;
[8]890
[83]891 if ( (!r)
[196]892 || (r == LEAF)
893 )
[8]894 return NULL;
895
[83]896 p = r;
897 if (p->left != LEAF)
898 return treeLast (p->left);
[196]899
900 p = r;
901 child = LEAF;
902 while ( (p->parent)
903 && (p->left == child)
904 )
[8]905 {
[196]906 child = p;
907 p = p->parent;
[8]908 }
[196]909
910 if (p->left != child)
911 return p;
912
913 return NULL;
[8]914}
915
[55]916/*
917 *@@ treeBuildArray:
918 * builds an array of TREE* pointers containing
919 * all tree items in sorted order.
920 *
921 * This returns a TREE** pointer to the array.
922 * Each item in the array is a TREE* pointer to
923 * the respective tree item.
924 *
925 * The array has been allocated using malloc()
926 * and must be free()'d by the caller.
927 *
928 * NOTE: This will only work if you maintain a
929 * tree node count yourself, which you must pass
930 * in *pulCount on input.
931 *
932 * This is most useful if you want to delete an
933 * entire tree without having to traverse it
934 * and rebalance the tree on every delete.
935 *
936 * Example usage for deletion:
937 *
938 + TREE *G_TreeRoot;
939 + treeInit(&G_TreeRoot);
940 +
941 + // add stuff to the tree
942 + TREE *pNewNode = malloc(...);
[83]943 + treeInsert(&G_TreeRoot, pNewNode, fnCompare)
[55]944 +
945 + // now delete all nodes
946 + ULONG cItems = ... // insert item count here
947 + TREE** papNodes = treeBuildArray(G_TreeRoot,
948 + &cItems);
949 + if (papNodes)
950 + {
951 + ULONG ul;
952 + for (ul = 0; ul < cItems; ul++)
953 + {
954 + TREE *pNodeThis = papNodes[ul];
955 + free(pNodeThis);
956 + }
957 +
958 + free(papNodes);
959 + }
960 +
961 *
962 *@@added V0.9.9 (2001-04-05) [umoeller]
963 */
[8]964
[55]965TREE** treeBuildArray(TREE* pRoot,
[113]966 PLONG plCount) // in: item count, out: array item count
[55]967{
968 TREE **papNodes = NULL,
969 **papThis = NULL;
[113]970 long cb = (sizeof(TREE*) * (*plCount)),
[55]971 cNodes = 0;
972
973 if (cb)
974 {
975 papNodes = (TREE**)malloc(cb);
976 papThis = papNodes;
977
978 if (papNodes)
979 {
980 TREE *pNode = (TREE*)treeFirst(pRoot);
981
982 memset(papNodes, 0, cb);
983
984 // copy nodes to array
985 while ( pNode
[113]986 && cNodes < (*plCount) // just to make sure
[55]987 )
988 {
989 *papThis = pNode;
990 cNodes++;
991 papThis++;
992
993 pNode = (TREE*)treeNext(pNode);
994 }
995
996 // output count
[113]997 *plCount = cNodes;
[55]998 }
999 }
1000
[238]1001 return papNodes;
[55]1002}
1003
[83]1004/* void main(int argc, char **argv) {
1005 int maxnum, ct;
1006 recType rec;
1007 keyType key;
1008 statusEnum status;
[55]1009
[83]1010 maxnum = atoi(argv[1]);
1011
1012 printf("maxnum = %d\n", maxnum);
1013 for (ct = maxnum; ct; ct--) {
1014 key = rand() % 9 + 1;
1015 if ((status = find(key, &rec)) == STATUS_OK) {
1016 status = delete(key);
1017 if (status) printf("fail: status = %d\n", status);
1018 } else {
1019 status = insert(key, &rec);
1020 if (status) printf("fail: status = %d\n", status);
1021 }
1022 }
1023} */
Note: See TracBrowser for help on using the repository browser.