source: trunk/include/k/kAvlTmpl/kAvlBase.h@ 2

Last change on this file since 2 was 2, checked in by bird, 18 years ago

Imported http://svn.netlabs.org/repos/libc/trunk/kStuff, revision 3612.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 19.2 KB
Line 
1/* $Id: kAvlBase.h 2 2007-11-16 16:07:14Z bird $ */
2/** @file
3 * kAvlTmpl - Templated AVL Trees, The Mandatory Base Code.
4 */
5
6/*
7 * Copyright (c) 2001-2007 knut st. osmundsen <bird-src-spam@anduin.net>
8 *
9 * This file is part of kStuff.
10 *
11 * kStuff is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * kStuff is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with kStuff; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 *
25 *
26 * As a special exception, since this is a source file and not a header
27 * file, you are granted permission to #include this file as you wish
28 * without this in itself causing the resulting program or whatever to be
29 * covered by the LGPL license. This exception does not however invalidate
30 * any other reasons why the resulting program/whatever should not be
31 * covered the LGPL or GPL.
32 */
33
34/** @page pg_kAvlTmpl Template Configuration.
35 *
36 * This is a templated implementation of AVL trees in C. The template
37 * parameters relates to the kind of key used and how duplicates are
38 * treated.
39 *
40 * \#define KAVL_EQUAL_ALLOWED
41 * Define this to tell us that equal keys are allowed.
42 * Then Equal keys will be put in a list pointed to by KAVLNODE::pList.
43 * This is by default not defined.
44 *
45 * \#define KAVL_CHECK_FOR_EQUAL_INSERT
46 * Define this to enable insert check for equal nodes.
47 * This is by default not defined.
48 *
49 * \#define KAVL_MAX_STACK
50 * Use this to specify the max number of stack entries the stack will use when inserting
51 * and removing nodes from the tree. The size should be something like
52 * log2(<max nodes>) + 3
53 * Must be defined.
54 *
55 * \#define KAVL_RANGE
56 * Define this to enable key ranges.
57 *
58 * \#define KAVL_OFFSET
59 * Define this to link the tree together using self relative offset
60 * instead of memory pointers, thus making the entire tree relocatable
61 * provided all the nodes - including the root node variable - are moved
62 * the exact same distance.
63 *
64 * \#define KAVLKEY
65 * Define this to the name of the AVL key type.
66 *
67 * \#define KAVL_STD_KEY_COMP
68 * Define this to use the standard key compare macros. If not set all the
69 * compare operations for KAVLKEY have to be defined: KAVL_G, KAVL_E, KAVL_NE,
70 * KAVL_R_IS_IDENTICAL, KAVL_R_IS_INTERSECTING and KAVL_R_IS_IN_RANGE. The latter
71 * three are only required when KAVL_RANGE is defined.
72 *
73 * \#define KAVLNODE
74 * Define this to the name (typedef) of the AVL node structure. This
75 * structure must have a mpLeft, mpRight, mKey and mHeight member.
76 * If KAVL_RANGE is defined a mKeyLast is also required.
77 * If KAVL_EQUAL_ALLOWED is defined a mpList member is required.
78 * It's possible to use other member names by redefining the names.
79 *
80 * \#define KAVLTREEPTR
81 * Define this to the name (typedef) of the tree pointer type. This is
82 * required when KAVL_OFFSET is defined. When not defined it defaults
83 * to KAVLNODE *.
84 *
85 * \#define KAVL_FN
86 * Use this to alter the names of the AVL functions.
87 * Must be defined.
88 *
89 * \#define KAVL_TYPE(prefix, name)
90 * Use this to make external type names and unique. The prefix may be empty.
91 * Must be defined.
92 *
93 * \#define KAVL_INT(name)
94 * Use this to make internal type names and unique. The prefix may be empty.
95 * Must be defined.
96 *
97 * \#define KAVL_DECL(rettype)
98 * Function declaration macro that should be set according to the scope
99 * the instantiated template should have. For instance an inlined scope
100 * (private or public) should K_DECL_INLINE(rettype) here.
101 *
102 * This version of the kAVL tree offers the option of inlining the entire
103 * implementation. This depends on the compiler doing a decent job in both
104 * making use of the inlined code and to eliminate const variables.
105 */
106
107
108/*******************************************************************************
109* Internal Functions *
110*******************************************************************************/
111#include <k/kDefs.h>
112#include <k/kTypes.h>
113#include <k/kHlpAssert.h>
114
115
116/*******************************************************************************
117* Defined Constants And Macros *
118*******************************************************************************/
119#define KAVL_HEIGHTOF(pNode) ((KU8)((pNode) != NULL ? (pNode)->mHeight : 0))
120
121/** @def KAVL_GET_POINTER
122 * Reads a 'pointer' value.
123 *
124 * @returns The native pointer.
125 * @param pp Pointer to the pointer to read.
126 * @internal
127 */
128
129/** @def KAVL_GET_POINTER_NULL
130 * Reads a 'pointer' value which can be KAVL_NULL.
131 *
132 * @returns The native pointer.
133 * @returns NULL pointer if KAVL_NULL.
134 * @param pp Pointer to the pointer to read.
135 * @internal
136 */
137
138/** @def KAVL_SET_POINTER
139 * Writes a 'pointer' value.
140 * For offset-based schemes offset relative to pp is calculated and assigned to *pp.
141 *
142 * @returns stored pointer.
143 * @param pp Pointer to where to store the pointer.
144 * @param p Native pointer to assign to *pp.
145 * @internal
146 */
147
148/** @def KAVL_SET_POINTER_NULL
149 * Writes a 'pointer' value which can be KAVL_NULL.
150 *
151 * For offset-based schemes offset relative to pp is calculated and assigned to *pp,
152 * if p is not KAVL_NULL of course.
153 *
154 * @returns stored pointer.
155 * @param pp Pointer to where to store the pointer.
156 * @param pp2 Pointer to where to pointer to assign to pp. This can be KAVL_NULL
157 * @internal
158 */
159
160#ifndef KAVLTREEPTR
161# define KAVLTREEPTR KAVLNODE *
162#endif
163
164#ifdef KAVL_OFFSET
165# define KAVL_GET_POINTER(pp) ( (KAVLNODE *)((KIPTR)(pp) + *(pp)) )
166# define KAVL_GET_POINTER_NULL(pp) ( *(pp) != KAVL_NULL ? KAVL_GET_POINTER(pp) : NULL )
167# define KAVL_SET_POINTER(pp, p) ( (*(pp)) = ((KIPTR)(p) - (KIPTR)(pp)) )
168# define KAVL_SET_POINTER_NULL(pp, pp2) ( (*(pp)) = *(pp2) != KAVL_NULL ? (KIPTR)KAVL_GET_POINTER(pp2) - (KIPTR)(pp) : KAVL_NULL )
169#else
170# define KAVL_GET_POINTER(pp) ( *(pp) )
171# define KAVL_GET_POINTER_NULL(pp) ( *(pp) )
172# define KAVL_SET_POINTER(pp, p) ( (*(pp)) = (p) )
173# define KAVL_SET_POINTER_NULL(pp, pp2) ( (*(pp)) = *(pp2) )
174#endif
175
176
177/** @def KAVL_NULL
178 * The NULL 'pointer' equivalent.
179 */
180#ifdef KAVL_OFFSET
181# define KAVL_NULL 0
182#else
183# define KAVL_NULL NULL
184#endif
185
186#ifdef KAVL_STD_KEY_COMP
187# define KAVL_G(key1, key2) ( (key1) > (key2) )
188# define KAVL_E(key1, key2) ( (key1) == (key2) )
189# define KAVL_NE(key1, key2) ( (key1) != (key2) )
190# ifdef KAVL_RANGE
191# define KAVL_R_IS_IDENTICAL(key1B, key2B, key1E, key2E) ( (key1B) == (key2B) && (key1E) == (key2E) )
192# define KAVL_R_IS_INTERSECTING(key1B, key2B, key1E, key2E) ( (key1B) <= (key2E) && (key1E) >= (key2B) )
193# define KAVL_R_IS_IN_RANGE(key1B, key1E, key2) KAVL_R_IS_INTERSECTING(key1B, key2, key1E, key2)
194# endif
195#endif
196
197#ifndef KAVL_RANGE
198# define KAVL_R_IS_INTERSECTING(key1B, key2B, key1E, key2E) KAVL_E(key1B, key2B)
199# define KAVL_R_IS_IDENTICAL(key1B, key2B, key1E, key2E) KAVL_E(key1B, key2B)
200#endif
201
202
203
204/*******************************************************************************
205* Structures and Typedefs *
206*******************************************************************************/
207/**
208 * Stack used to avoid recursive calls during insert and removal.
209 */
210typedef struct
211{
212 unsigned cEntries;
213 KAVLTREEPTR *aEntries[KAVL_MAX_STACK];
214} KAVL_INT(STACK);
215
216/**
217 * The callback used by the Destroy and DoWithAll functions.
218 */
219typedef int (* KAVL_TYPE(PFN,CALLBACK))(KAVLNODE *, void *);
220
221
222
223/**
224 * Rewinds a stack of node pointer pointers, rebalancing the tree.
225 *
226 * @param pStack Pointer to stack to rewind.
227 * @sketch LOOP thru all stack entries
228 * BEGIN
229 * Get pointer to pointer to node (and pointer to node) from the stack.
230 * IF 2 higher left subtree than in right subtree THEN
231 * BEGIN
232 * IF higher (or equal) left-sub-subtree than right-sub-subtree THEN
233 * * n+2|n+3
234 * / \ / \
235 * n+2 n ==> n+1 n+1|n+2
236 * / \ / \
237 * n+1 n|n+1 n|n+1 n
238 *
239 * Or with keys:
240 *
241 * 4 2
242 * / \ / \
243 * 2 5 ==> 1 4
244 * / \ / \
245 * 1 3 3 5
246 *
247 * ELSE
248 * * n+2
249 * / \ / \
250 * n+2 n n+1 n+1
251 * / \ ==> / \ / \
252 * n n+1 n L R n
253 * / \
254 * L R
255 *
256 * Or with keys:
257 * 6 4
258 * / \ / \
259 * 2 7 ==> 2 6
260 * / \ / \ / \
261 * 1 4 1 3 5 7
262 * / \
263 * 3 5
264 * END
265 * ELSE IF 2 higher in right subtree than in left subtree THEN
266 * BEGIN
267 * Same as above but left <==> right. (invert the picture)
268 * ELSE
269 * IF correct height THEN break
270 * ELSE correct height.
271 * END
272 */
273K_DECL_INLINE(void) KAVL_FN(Rebalance)(KAVL_INT(STACK) *pStack)
274{
275 while (pStack->cEntries > 0)
276 {
277 KAVLTREEPTR *ppNode = pStack->aEntries[--pStack->cEntries];
278 KAVLNODE *pNode = KAVL_GET_POINTER(ppNode);
279 KAVLNODE *pLeftNode = KAVL_GET_POINTER_NULL(&pNode->mpLeft);
280 KU8 uLeftHeight = KAVL_HEIGHTOF(pLeftNode);
281 KAVLNODE *pRightNode = KAVL_GET_POINTER_NULL(&pNode->mpRight);
282 KU8 uRightHeight = KAVL_HEIGHTOF(pRightNode);
283
284 if (uRightHeight + 1 < uLeftHeight)
285 {
286 KAVLNODE *pLeftLeftNode = KAVL_GET_POINTER_NULL(&pLeftNode->mpLeft);
287 KAVLNODE *pLeftRightNode = KAVL_GET_POINTER_NULL(&pLeftNode->mpRight);
288 KU8 uLeftRightHeight = KAVL_HEIGHTOF(pLeftRightNode);
289
290 if (KAVL_HEIGHTOF(pLeftLeftNode) >= uLeftRightHeight)
291 {
292 KAVL_SET_POINTER_NULL(&pNode->mpLeft, &pLeftNode->mpRight);
293 KAVL_SET_POINTER(&pLeftNode->mpRight, pNode);
294 pLeftNode->mHeight = (KU8)(1 + (pNode->mHeight = (KU8)(1 + uLeftRightHeight)));
295 KAVL_SET_POINTER(ppNode, pLeftNode);
296 }
297 else
298 {
299 KAVL_SET_POINTER_NULL(&pLeftNode->mpRight, &pLeftRightNode->mpLeft);
300 KAVL_SET_POINTER_NULL(&pNode->mpLeft, &pLeftRightNode->mpRight);
301 KAVL_SET_POINTER(&pLeftRightNode->mpLeft, pLeftNode);
302 KAVL_SET_POINTER(&pLeftRightNode->mpRight, pNode);
303 pLeftNode->mHeight = pNode->mHeight = uLeftRightHeight;
304 pLeftRightNode->mHeight = uLeftHeight;
305 KAVL_SET_POINTER(ppNode, pLeftRightNode);
306 }
307 }
308 else if (uLeftHeight + 1 < uRightHeight)
309 {
310 KAVLNODE *pRightLeftNode = KAVL_GET_POINTER_NULL(&pRightNode->mpLeft);
311 KU8 uRightLeftHeight = KAVL_HEIGHTOF(pRightLeftNode);
312 KAVLNODE *pRightRightNode = KAVL_GET_POINTER_NULL(&pRightNode->mpRight);
313
314 if (KAVL_HEIGHTOF(pRightRightNode) >= uRightLeftHeight)
315 {
316 KAVL_SET_POINTER_NULL(&pNode->mpRight, &pRightNode->mpLeft);
317 KAVL_SET_POINTER(&pRightNode->mpLeft, pNode);
318 pRightNode->mHeight = (KU8)(1 + (pNode->mHeight = (KU8)(1 + uRightLeftHeight)));
319 KAVL_SET_POINTER(ppNode, pRightNode);
320 }
321 else
322 {
323 KAVL_SET_POINTER_NULL(&pRightNode->mpLeft, &pRightLeftNode->mpRight);
324 KAVL_SET_POINTER_NULL(&pNode->mpRight, &pRightLeftNode->mpLeft);
325 KAVL_SET_POINTER(&pRightLeftNode->mpRight, pRightNode);
326 KAVL_SET_POINTER(&pRightLeftNode->mpLeft, pNode);
327 pRightNode->mHeight = pNode->mHeight = uRightLeftHeight;
328 pRightLeftNode->mHeight = uRightHeight;
329 KAVL_SET_POINTER(ppNode, pRightLeftNode);
330 }
331 }
332 else
333 {
334 KU8 uHeight = (KU8)(K_MAX(uLeftHeight, uRightHeight) + 1);
335 if (uHeight == pNode->mHeight)
336 break;
337 pNode->mHeight = uHeight;
338 }
339 }
340
341}
342
343
344/**
345 * Inserts a node into the AVL-tree.
346 * @returns TRUE if inserted.
347 * FALSE if node exists in tree.
348 * @param ppTree Pointer to the AVL-tree root node pointer.
349 * @param pNode Pointer to the node which is to be added.
350 * @sketch Find the location of the node (using binary tree algorithm.):
351 * LOOP until NULL leaf pointer
352 * BEGIN
353 * Add node pointer pointer to the AVL-stack.
354 * IF new-node-key < node key THEN
355 * left
356 * ELSE
357 * right
358 * END
359 * Fill in leaf node and insert it.
360 * Rebalance the tree.
361 */
362KAVL_DECL(KBOOL) KAVL_FN(Insert)(KAVLTREEPTR *ppTree, KAVLNODE *pNode)
363{
364 KAVL_INT(STACK) AVLStack;
365 KAVLTREEPTR *ppCurNode = ppTree;
366 register KAVLKEY Key = pNode->mKey;
367#ifdef KAVL_RANGE
368 register KAVLKEY KeyLast = pNode->mKeyLast;
369#endif
370
371#ifdef KAVL_RANGE
372 if (Key > KeyLast)
373 return false;
374#endif
375
376 AVLStack.cEntries = 0;
377 while (*ppCurNode != KAVL_NULL)
378 {
379 register KAVLNODE *pCurNode = KAVL_GET_POINTER(ppCurNode);
380
381 kHlpAssert(AVLStack.cEntries < KAVL_MAX_STACK);
382 AVLStack.aEntries[AVLStack.cEntries++] = ppCurNode;
383#ifdef KAVL_EQUAL_ALLOWED
384 if (KAVL_R_IS_IDENTICAL(pCurNode->mKey, Key, pCurNode->mKeyLast, KeyLast))
385 {
386 /*
387 * If equal then we'll use a list of equal nodes.
388 */
389 pNode->mpLeft = pNode->mpRight = KAVL_NULL;
390 pNode->mHeight = 0;
391 KAVL_SET_POINTER_NULL(&pNode->mpList, &pCurNode->mpList);
392 KAVL_SET_POINTER(&pCurNode->mpList, pNode);
393 return K_TRUE;
394 }
395#endif
396#ifdef KAVL_CHECK_FOR_EQUAL_INSERT
397 if (KAVL_R_IS_INTERSECTING(pCurNode->mKey, Key, pCurNode->mKeyLast, KeyLast))
398 return K_FALSE;
399#endif
400 if (KAVL_G(pCurNode->mKey, Key))
401 ppCurNode = &pCurNode->mpLeft;
402 else
403 ppCurNode = &pCurNode->mpRight;
404 }
405
406 pNode->mpLeft = pNode->mpRight = KAVL_NULL;
407#ifdef KAVL_EQUAL_ALLOWED
408 pNode->mpList = KAVL_NULL;
409#endif
410 pNode->mHeight = 1;
411 KAVL_SET_POINTER(ppCurNode, pNode);
412
413 KAVL_FN(Rebalance)(&AVLStack);
414 return K_TRUE;
415}
416
417
418/**
419 * Removes a node from the AVL-tree.
420 * @returns Pointer to the node.
421 * @param ppTree Pointer to the AVL-tree root node pointer.
422 * @param Key Key value of the node which is to be removed.
423 * @sketch Find the node which is to be removed:
424 * LOOP until not found
425 * BEGIN
426 * Add node pointer pointer to the AVL-stack.
427 * IF the keys matches THEN break!
428 * IF remove key < node key THEN
429 * left
430 * ELSE
431 * right
432 * END
433 * IF found THEN
434 * BEGIN
435 * IF left node not empty THEN
436 * BEGIN
437 * Find the right most node in the left tree while adding the pointer to the pointer to it's parent to the stack:
438 * Start at left node.
439 * LOOP until right node is empty
440 * BEGIN
441 * Add to stack.
442 * go right.
443 * END
444 * Link out the found node.
445 * Replace the node which is to be removed with the found node.
446 * Correct the stack entry for the pointer to the left tree.
447 * END
448 * ELSE
449 * BEGIN
450 * Move up right node.
451 * Remove last stack entry.
452 * END
453 * Balance tree using stack.
454 * END
455 * return pointer to the removed node (if found).
456 */
457KAVL_DECL(KAVLNODE *) KAVL_FN(Remove)(KAVLTREEPTR *ppTree, KAVLKEY Key)
458{
459 KAVL_INT(STACK) AVLStack;
460 KAVLTREEPTR *ppDeleteNode = ppTree;
461 register KAVLNODE *pDeleteNode;
462
463 AVLStack.cEntries = 0;
464 for (;;)
465 {
466 if (*ppDeleteNode == KAVL_NULL)
467 return NULL;
468 pDeleteNode = KAVL_GET_POINTER(ppDeleteNode);
469
470 kHlpAssert(AVLStack.cEntries < KAVL_MAX_STACK);
471 AVLStack.aEntries[AVLStack.cEntries++] = ppDeleteNode;
472 if (KAVL_E(pDeleteNode->mKey, Key))
473 break;
474
475 if (KAVL_G(pDeleteNode->mKey, Key))
476 ppDeleteNode = &pDeleteNode->mpLeft;
477 else
478 ppDeleteNode = &pDeleteNode->mpRight;
479 }
480
481 if (pDeleteNode->mpLeft != KAVL_NULL)
482 {
483 /* find the rightmost node in the left tree. */
484 const unsigned iStackEntry = AVLStack.cEntries;
485 KAVLTREEPTR *ppLeftLeast = &pDeleteNode->mpLeft;
486 register KAVLNODE *pLeftLeast = KAVL_GET_POINTER(ppLeftLeast);
487
488 while (pLeftLeast->mpRight != KAVL_NULL)
489 {
490 kHlpAssert(AVLStack.cEntries < KAVL_MAX_STACK);
491 AVLStack.aEntries[AVLStack.cEntries++] = ppLeftLeast;
492 ppLeftLeast = &pLeftLeast->mpRight;
493 pLeftLeast = KAVL_GET_POINTER(ppLeftLeast);
494 }
495
496 /* link out pLeftLeast */
497 KAVL_SET_POINTER_NULL(ppLeftLeast, &pLeftLeast->mpLeft);
498
499 /* link it in place of the delete node. */
500 KAVL_SET_POINTER_NULL(&pLeftLeast->mpLeft, &pDeleteNode->mpLeft);
501 KAVL_SET_POINTER_NULL(&pLeftLeast->mpRight, &pDeleteNode->mpRight);
502 pLeftLeast->mHeight = pDeleteNode->mHeight;
503 KAVL_SET_POINTER(ppDeleteNode, pLeftLeast);
504 AVLStack.aEntries[iStackEntry] = &pLeftLeast->mpLeft;
505 }
506 else
507 {
508 KAVL_SET_POINTER_NULL(ppDeleteNode, &pDeleteNode->mpRight);
509 AVLStack.cEntries--;
510 }
511
512 KAVL_FN(Rebalance)(&AVLStack);
513 return pDeleteNode;
514}
515
Note: See TracBrowser for help on using the repository browser.