[GRASS-SVN] r36516 - grass/trunk/lib/vector/diglib

svn_grass at osgeo.org svn_grass at osgeo.org
Sun Mar 29 15:46:31 EDT 2009


Author: mmetz
Date: 2009-03-29 15:46:31 -0400 (Sun, 29 Mar 2009)
New Revision: 36516

Modified:
   grass/trunk/lib/vector/diglib/rbtree.c
Log:
Red Black Tree polished, functions

Modified: grass/trunk/lib/vector/diglib/rbtree.c
===================================================================
--- grass/trunk/lib/vector/diglib/rbtree.c	2009-03-29 19:45:35 UTC (rev 36515)
+++ grass/trunk/lib/vector/diglib/rbtree.c	2009-03-29 19:46:31 UTC (rev 36516)
@@ -1,34 +1,33 @@
-/****************************************************************************
+/*!
+ * \file rbtree.c
  *
- * MODULE:       Vector library 
- *              
- * AUTHOR(S):    Markus Metz
+ * \brief binary search tree 
  *
- * PURPOSE:      Lower level functions for reading/writing/manipulating vectors.
+ * Generic balanced binary search tree (Red Black Tree) implementation
  *
- * COPYRIGHT:    (C) 2009 by the GRASS Development Team
+ * (C) 2009 by the GRASS Development Team
  *
- *               This program is free software under the GNU General Public
- *               License (>=v2). Read the file COPYING that comes with GRASS
- *               for details.
+ * This program is free software under the GNU General Public License
+ * (>=v2).  Read the file COPYING that comes with GRASS for details.
  *
- *****************************************************************************/
+ * \author Original author Julienne Walker 2003, 2008
+ *         GRASS implementation Markus Metz, 2009
+ */
 
 /* balanced binary search tree implementation
+ * 
  * this one is a Red Black Tree, the bare version, no parent pointers, no threads
- * The core code comes from Julienne Walker's tutorials on
- * binary search trees: insert, remove, balance
- * support for any kind of data structures comes from libavl (GPL >= 2)
- *
+ * The core code comes from Julienne Walker's tutorials on binary search trees
+ * original license: public domain
+ * http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx
+ * some ideas come from libavl (GPL >= 2)
  * I could have used some off-the-shelf solution, but that's boring
  *
- * Red Black Trees are used to maintain a data structure that allows
+ * Red Black Trees are used to maintain a data structure with
  * search, insertion and deletion in O(log N) time
- * This is needed for large vectors with many features
  */
 
 #include <assert.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <grass/gis.h>
@@ -46,18 +45,19 @@
 
 
 /* create new tree and initialize
- * return pointer to new tree or NULL for memory allocation error
+ * returns pointer to new tree, NULL for memory allocation error
  */
-
 struct RB_TREE *rbtree_create(rb_compare_fn *compare, size_t rb_datasize)
 {
     struct RB_TREE *tree = malloc(sizeof(*tree));
 
     if (tree == NULL) {
-	G_warning("RB Search Tree: Out of memory!");
+	G_warning("RB tree: Out of memory!");
 	return NULL;
     }
 
+    assert(compare);
+
     tree->datasize = rb_datasize;
     tree->rb_compare = compare;
     tree->count = 0;
@@ -67,12 +67,14 @@
 } 
 
 /* add an item to a tree
- * returns 1 on success, 0 on failure
  * non-recursive top-down insertion
  * the algorithm does not allow duplicates and also does not warn about a duplicate
+ * returns 1 on success, 0 on failure
  */
 int rbtree_insert(struct RB_TREE *tree, void *data)
 {
+    assert(tree && data);
+    
     if (tree->root == NULL) {
 	/* create a new root node for tree */
 	tree->root = rbtree_make_node(tree->datasize, data);
@@ -119,11 +121,13 @@
 	    last = dir;
 	    dir = tree->rb_compare(q->data, data);
 
-	    /* Stop if found */
-	    if (dir == 2)
+	    /* Stop if found. This check also disallows duplicates in the tree */
+	    if (dir == 0)
 		break;
 
-	    /* Update helpers */
+	    dir = dir < 0;
+
+	    /* Move the helpers down */
 	    if (g != NULL)
 		t = g;
 
@@ -143,18 +147,20 @@
     return 1;
 }
 
-/* delete an item from a tree
- * returns 1 on successful deletion
+/* remove an item from a tree that matches given data
+ * non-recursive top-down removal
+ * returns 1 on successful removal
  * returns 0 if data item was not found
- * non-recursive top-down deletion
  */
 int rbtree_remove(struct RB_TREE *tree, const void *data)
 {
     struct RB_NODE head = {0}; /* False tree root */
     struct RB_NODE *q, *p, *g; /* Helpers */
     struct RB_NODE *f = NULL;  /* Found item */
-    int dir = 1, found = 0;
+    int dir = 1, removed = 0;
 
+    assert(tree && data);
+
     if (tree->root == NULL) {
 	return 0; /* empty tree, nothing to remove */
     }
@@ -174,11 +180,11 @@
 	dir = tree->rb_compare(q->data, data);
 
 	/* Save found node */
-	if (dir == 2) {
+	if (dir == 0)
 	    f = q;
-	    dir = 0;
-	}
 
+	dir = dir < 0;
+
 	/* Push the red node down */
 	if (!is_red(q) && !is_red(q->link[dir])) {
 	    if (is_red(q->link[!dir]))
@@ -214,39 +220,40 @@
 
     /* Replace and remove if found */
     if (f != NULL) {
+	free(f->data);
+	f->data = q->data;
 	p->link[p->link[1] == q] = q->link[q->link[0] == NULL];
-	free(q->data);
 	free(q);
 	tree->count--;
-	found = 1;
+	removed = 1;
     }
     else
-	G_debug(2, "data not found in search tree");
+	G_debug(2, "RB tree: data not found in search tree");
 
     /* Update root and make it black */
     tree->root = head.link[1];
     if ( tree->root != NULL)
 	tree->root->red = 0;
 
-    return found;
+    return removed;
 }
 
 /* find data item in tree
- * return pointer to data item if found else NULL
+ * returns pointer to data item if found else NULL
  */
 void *rbtree_find(struct RB_TREE *tree, const void *data)
 {
     struct RB_NODE *curr_node = tree->root;
-    int dir = 0;
+    int cmp = 0;
 
     assert(tree && data);
 
     while (curr_node != NULL) {
-	dir = tree->rb_compare(curr_node->data, data);
-	if (dir == 2)
-	    return curr_node->data;
+	cmp = tree->rb_compare(curr_node->data, data);
+	if (cmp == 0)
+	    return curr_node->data;   /* found */
 	else {
-	    curr_node = curr_node->link[dir];
+	    curr_node = curr_node->link[cmp < 0];
 	}
     }
     return NULL;
@@ -254,9 +261,9 @@
 
 /* initialize tree traversal
  * (re-)sets trav structure
- * return pointer to trav struct or NULL on memory allocation error
+ * returns 0
  */
-void rbtree_init_trav(struct RB_TRAV *trav, struct RB_TREE *tree)
+int rbtree_init_trav(struct RB_TRAV *trav, struct RB_TREE *tree)
 {
     int i;
 
@@ -268,21 +275,24 @@
 
     for (i = 0; i < RBTREE_MAX_HEIGHT; i++)
 	trav->up[i] = NULL;
+
+    return 0;
 }
 
 /* traverse the tree in ascending order
  * useful to get all items in the tree non-recursively
- * return pointer to data
  * struct RB_TRAV *trav needs to be initialized first
+ * returns pointer to data, NULL when finished
  */
 void *rbtree_traverse(struct RB_TRAV *trav)
 {
     assert(trav);
+    
     if (trav->curr_node == NULL) {
 	if (trav->first)
-	    G_warning("empty tree");
+	    G_warning("RB tree: empty tree");
 	else
-	    G_warning("finished traversing");
+	    G_warning("RB tree: finished traversing");
 
 	return NULL;
     }
@@ -295,6 +305,53 @@
 	return rbtree_next(trav);
 }
 
+/* find start point to traverse the tree in ascending order
+ * useful to get a selection of items in the tree
+ * magnitudes faster than traversing the whole tree
+ * may return first item that's smaller or first item that's larger
+ * struct RB_TRAV *trav needs to be initialized first
+ * returns pointer to data, NULL on error
+ */
+void *rbtree_traverse_start(struct RB_TRAV *trav, const void *data)
+{
+    int dir = 0;
+
+    assert(trav && data);
+
+    if (trav->first == 0) {
+	G_debug(1, "RB tree: trav must be initialised first");
+	return NULL;
+    }
+
+    if (trav->curr_node == NULL) {
+	G_warning("RB tree: empty tree");
+	return NULL;
+    }
+	
+    trav->first = 0;
+    trav->top = 0;
+
+    while (trav->curr_node != NULL) {
+	dir = trav->tree->rb_compare(trav->curr_node->data, data);
+	/* exact match, great! */
+	if (dir == 0)
+	    return trav->curr_node->data;
+	else {
+	    dir = dir < 0;
+	    /* end of branch, also reached if
+	     * smallest item is larger than search template or
+	     * largest item is smaller than search template */
+	    if (trav->curr_node->link[dir] == NULL)
+		return trav->curr_node->data;
+		
+	    trav->up[trav->top++] = trav->curr_node;
+	    trav->curr_node = trav->curr_node->link[dir];
+	}
+    }
+
+    return NULL; /* should not happen */
+}
+
 /* two functions needed to fully traverse the tree: initialize and continue
  * useful to get all items in the tree non-recursively
  * this one here uses a stack
@@ -303,27 +360,25 @@
  * -> more memory needed for standard operations
  */
 
-/* start traversing the tree */
+/* start traversing the tree
+ * returns pointer to smallest data item
+ */
 void *rbtree_first(struct RB_TRAV *trav)
 {
     trav->top = 0;
 
     /* get smallest item */
-    if (trav->curr_node != NULL) {
-	while (trav->curr_node->link[0] != NULL) {
-	    trav->up[trav->top++] = trav->curr_node;
-	    trav->curr_node = trav->curr_node->link[0];
-	}
+    while (trav->curr_node->link[0] != NULL) {
+	trav->up[trav->top++] = trav->curr_node;
+	trav->curr_node = trav->curr_node->link[0];
     }
 
-    if (trav->curr_node != NULL) {
-	return trav->curr_node->data; /* return smallest item */
-    }
-    else
-	return NULL; /* empty tree */
+    return trav->curr_node->data; /* return smallest item */
 }
 
-/* continue traversing the tree */
+/* continue traversing the tree in ascending order
+ * returns pointer to data item, NULL when finished
+ */
 void *rbtree_next(struct RB_TRAV *trav)
 {
     if (trav->curr_node->link[1] != NULL) {
@@ -360,6 +415,7 @@
 /* destroy the tree */
 void rbtree_destroy(struct RB_TREE *tree) {
     rbtree_destroy2(tree->root);
+    free(tree);
 }
 
 void rbtree_destroy2(struct RB_NODE *root)
@@ -372,63 +428,7 @@
     }
 }
 
-/*!
- * internal funtions used for Red Black Tree maintenance
- */
-
-/* add a new node to the tree */
-struct RB_NODE *rbtree_make_node(size_t datasize, void *data)
-{
-    struct RB_NODE *new_node = malloc(sizeof(*new_node));
-
-    if (new_node != NULL) {
-	new_node->data = malloc(datasize);
-	if (new_node->data == NULL)
-	    G_fatal_error("RB Search Tree: Out of memory!");
-	    
-	memcpy(new_node->data, data, datasize);
-	new_node->red = 1; /* 1 is red, 0 is black */
-	new_node->link[0] = NULL;
-	new_node->link[1] = NULL;
-    }
-    else
-	G_fatal_error("RB Search Tree: Out of memory!");
-
-    return new_node;
-}
-
-/* check for red violation */
-int is_red(struct RB_NODE *root)
-{
-    if (root)
-	return root->red == 1;
-
-    return 0;
-}
-
-/* single rotation */
-struct RB_NODE *rbtree_single(struct RB_NODE *root, int dir)
-{
-    struct RB_NODE *newroot = root->link[!dir];
-
-    root->link[!dir] = newroot->link[dir];
-    newroot->link[dir] = root;
-
-    root->red = 1;
-    newroot->red = 0;
-
-    return newroot;
-}
- 
-/* double rotation */
-struct RB_NODE *rbtree_double(struct RB_NODE *root, int dir)
-{
-    root->link[!dir] = rbtree_single(root->link[!dir], !dir);
-    return rbtree_single(root, dir);
-}
-
-/* only used for debugging */
-/* check for errors */
+/* used for debugging: check for errors in tree structure */
 int rbtree_debug(struct RB_TREE *tree, struct RB_NODE *root)
 {
     int lh, rh;
@@ -438,7 +438,7 @@
     else {
 	struct RB_NODE *ln = root->link[0];
 	struct RB_NODE *rn = root->link[1];
-	int lcmp, rcmp;
+	int lcmp = 0, rcmp = 0;
 
 	/* Consecutive red links */
 	if (is_red(root)) {
@@ -454,21 +454,15 @@
 	if (ln) {
 	    lcmp = tree->rb_compare(ln->data, root->data);
 	}
-	else {
-	    lcmp = 1;
-	}
 	
 	if (rn) {
 	    rcmp = tree->rb_compare(rn->data, root->data);
 	}
-	else {
-	    rcmp = 1;
-	}
-	
 
-	/* Invalid binary search tree */
-	if ((ln != NULL && (lcmp == 0 || lcmp == 2))
-	 || (rn != NULL && (rcmp == 1 || rcmp == 2))) {
+	/* Invalid binary search tree:
+	 * left node >= parent or right node <= parent */
+	if ((ln != NULL && lcmp > -1)
+	 || (rn != NULL && rcmp < 1)) {
 	    G_warning("Red Black Tree debugging: Binary tree violation" );
 	    return 0;
 	}
@@ -486,3 +480,59 @@
 	    return 0;
     }
 }
+
+/*******************************************************
+ *                                                     *
+ *  internal functions for Red Black Tree maintenance  *
+ *                                                     *
+ *******************************************************/
+
+/* add a new node to the tree */
+struct RB_NODE *rbtree_make_node(size_t datasize, void *data)
+{
+    struct RB_NODE *new_node = malloc(sizeof(*new_node));
+
+    if (new_node == NULL)
+	G_fatal_error("RB Search Tree: Out of memory!");
+
+    new_node->data = malloc(datasize);
+    if (new_node->data == NULL)
+	G_fatal_error("RB Search Tree: Out of memory!");
+	
+    memcpy(new_node->data, data, datasize);
+    new_node->red = 1;            /* 1 is red, 0 is black */
+    new_node->link[0] = NULL;
+    new_node->link[1] = NULL;
+
+    return new_node;
+}
+
+/* check for red violation */
+int is_red(struct RB_NODE *root)
+{
+    if (root)
+	return root->red == 1;
+
+    return 0;
+}
+
+/* single rotation */
+struct RB_NODE *rbtree_single(struct RB_NODE *root, int dir)
+{
+    struct RB_NODE *newroot = root->link[!dir];
+
+    root->link[!dir] = newroot->link[dir];
+    newroot->link[dir] = root;
+
+    root->red = 1;
+    newroot->red = 0;
+
+    return newroot;
+}
+ 
+/* double rotation */
+struct RB_NODE *rbtree_double(struct RB_NODE *root, int dir)
+{
+    root->link[!dir] = rbtree_single(root->link[!dir], !dir);
+    return rbtree_single(root, dir);
+}



More information about the grass-commit mailing list