Implement GTree with a treap instead of an AVL tree
authorDana Jansens <danakj@orodu.net>
Wed, 28 Oct 2009 18:49:25 +0000 (14:49 -0400)
committerDana Jansens <danakj@orodu.net>
Wed, 25 Nov 2009 17:01:11 +0000 (12:01 -0500)
glib/gtree.c
tests/tree-test.c

index fd984ee..6c69c88 100644 (file)
@@ -35,8 +35,6 @@
 
 #undef G_TREE_DEBUG
 
-#define MAX_GTREE_HEIGHT 40
-
 typedef struct _GTreeNode  GTreeNode;
 
 struct _GTree
@@ -56,14 +54,17 @@ struct _GTreeNode
   gpointer   value;       /* value stored at this node */
   GTreeNode *left;        /* left subtree */
   GTreeNode *right;       /* right subtree */
-  gint8      balance;     /* height (left) - height (right) */
-  guint8     left_child;  
-  guint8     right_child;
+  GTreeNode *parent;      /* parent node */
+  guint8     left_child;  /* the node has a left child (else left points to the
+                             previous node in the tree) */
+  guint8     right_child; /* the node has a right child (else right points to
+                             the next node in the tree) */
 };
 
 
 static GTreeNode* g_tree_node_new                   (gpointer       key,
                                                     gpointer       value);
+static guint      g_tree_priority                   (GTreeNode     *node);
 static void       g_tree_insert_internal            (GTree         *tree,
                                                     gpointer       key,
                                                     gpointer       value,
@@ -71,7 +72,6 @@ static void       g_tree_insert_internal            (GTree         *tree,
 static gboolean   g_tree_remove_internal            (GTree         *tree,
                                                     gconstpointer  key,
                                                     gboolean       steal);
-static GTreeNode* g_tree_node_balance               (GTreeNode     *node);
 static GTreeNode *g_tree_find_node                  (GTree         *tree,
                                                     gconstpointer  key);
 static gint       g_tree_node_pre_order             (GTreeNode     *node,
@@ -86,22 +86,22 @@ static gint       g_tree_node_post_order            (GTreeNode     *node,
 static gpointer   g_tree_node_search                (GTreeNode     *node,
                                                     GCompareFunc   search_func,
                                                     gconstpointer  data);
+static gint       g_tree_node_height                (GTreeNode     *node);
 static GTreeNode* g_tree_node_rotate_left           (GTreeNode     *node);
 static GTreeNode* g_tree_node_rotate_right          (GTreeNode     *node);
 #ifdef G_TREE_DEBUG
 static void       g_tree_node_check                 (GTreeNode     *node);
 #endif
 
-
 static GTreeNode*
 g_tree_node_new (gpointer key,
                 gpointer value)
 {
   GTreeNode *node = g_slice_new (GTreeNode);
 
-  node->balance = 0;
   node->left = NULL;
   node->right = NULL;
+  node->parent = NULL;
   node->left_child = FALSE;
   node->right_child = FALSE;
   node->key = key;
@@ -340,7 +340,7 @@ g_tree_destroy (GTree *tree)
  * creating the #GTree, the passed key is freed using that function.
  *
  * The tree is automatically 'balanced' as new key/value pairs are added,
- * so that the distance from the root to every leaf is as small as possible.
+ * so that the distance from the root to every leaf is small.
  **/
 void
 g_tree_insert (GTree    *tree,
@@ -370,7 +370,7 @@ g_tree_insert (GTree    *tree,
  * freed using that function. 
  *
  * The tree is automatically 'balanced' as new key/value pairs are added,
- * so that the distance from the root to every leaf is as small as possible.
+ * so that the distance from the root to every leaf is small.
  **/
 void
 g_tree_replace (GTree    *tree,
@@ -386,6 +386,33 @@ g_tree_replace (GTree    *tree,
 #endif
 }
 
+/*
+ * Implementation of a treap
+ *
+ * (Copied from gsequence.c)
+ */
+static guint
+g_tree_priority (GTreeNode *node)
+{
+  guint key = GPOINTER_TO_UINT (node);
+
+  /* This hash function is based on one found on Thomas Wang's
+   * web page at
+   *
+   *    http://www.concentric.net/~Ttwang/tech/inthash.htm
+   *
+   */
+  key = (key << 15) - key - 1;
+  key = key ^ (key >> 12);
+  key = key + (key << 2);
+  key = key ^ (key >> 4);
+  key = key + (key << 3) + (key << 11);
+  key = key ^ (key >> 16);
+
+  /* We rely on 0 being less than all other priorities */
+  return key? key : 1;
+}
+
 /* internal insert routine */
 static void
 g_tree_insert_internal (GTree    *tree,
@@ -393,9 +420,7 @@ g_tree_insert_internal (GTree    *tree,
                         gpointer  value,
                         gboolean  replace)
 {
-  GTreeNode *node;
-  GTreeNode *path[MAX_GTREE_HEIGHT];
-  int idx;
+  GTreeNode *node, *child;
 
   g_return_if_fail (tree != NULL);
 
@@ -406,13 +431,11 @@ g_tree_insert_internal (GTree    *tree,
       return;
     }
 
-  idx = 0;
-  path[idx++] = NULL;
   node = tree->root;
 
   while (1)
     {
-      int cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+      gint cmp = tree->key_compare (key, node->key, tree->key_compare_data);
       
       if (cmp == 0)
         {
@@ -440,19 +463,17 @@ g_tree_insert_internal (GTree    *tree,
       else if (cmp < 0)
         {
           if (node->left_child)
-            {
-              path[idx++] = node;
               node = node->left;
-            }
           else
             {
-              GTreeNode *child = g_tree_node_new (key, value);
+              child = g_tree_node_new (key, value);
 
               child->left = node->left;
               child->right = node;
+              child->parent = node;
+
               node->left = child;
               node->left_child = TRUE;
-              node->balance -= 1;
 
              tree->nnodes++;
 
@@ -462,19 +483,17 @@ g_tree_insert_internal (GTree    *tree,
       else
         {
           if (node->right_child)
-            {
-              path[idx++] = node;
               node = node->right;
-            }
           else
             {
-              GTreeNode *child = g_tree_node_new (key, value);
+              child = g_tree_node_new (key, value);
 
               child->right = node->right;
               child->left = node;
+              child->parent = node;
+
               node->right = child;
               node->right_child = TRUE;
-              node->balance += 1;
 
              tree->nnodes++;
 
@@ -483,35 +502,24 @@ g_tree_insert_internal (GTree    *tree,
         }
     }
 
-  /* restore balance. This is the goodness of a non-recursive
-     implementation, when we are done with balancing we 'break'
-     the loop and we are done. */
-  while (1)
+  /* rotate the new node up until the heap property is restored */
+  node = child;
+  while (node->parent &&
+         g_tree_priority (node) < g_tree_priority (node->parent))
     {
-      GTreeNode *bparent = path[--idx];
-      gboolean left_node = (bparent && node == bparent->left);
-      g_assert (!bparent || bparent->left == node || bparent->right == node);
-
-      if (node->balance < -1 || node->balance > 1)
-        {
-          node = g_tree_node_balance (node);
-          if (bparent == NULL)
-            tree->root = node;
-          else if (left_node)
-            bparent->left = node;
-          else
-            bparent->right = node;
-        }
-
-      if (node->balance == 0 || bparent == NULL)
-        break;
-      
-      if (left_node)
-        bparent->balance -= 1;
+      GTreeNode *const sp = node->parent->parent;
+      GTreeNode *const p = node->parent;
+      if (p->left == node)
+        g_tree_node_rotate_right (p);
       else
-        bparent->balance += 1;
+        g_tree_node_rotate_left (p);
 
-      node = bparent;
+      if (!sp)
+        tree->root = node;
+      else if (sp->left == p)
+        sp->left = node;
+      else
+        sp->right = node;
     }
 }
 
@@ -583,23 +591,21 @@ g_tree_remove_internal (GTree         *tree,
                         gconstpointer  key,
                         gboolean       steal)
 {
-  GTreeNode *node, *parent, *balance;
-  GTreeNode *path[MAX_GTREE_HEIGHT];
-  int idx;
-  gboolean left_node;
+  GTreeNode *node, *parent;
+  gboolean is_leftchild;
 
   g_return_val_if_fail (tree != NULL, FALSE);
 
   if (!tree->root)
     return FALSE;
 
-  idx = 0;
-  path[idx++] = NULL;
   node = tree->root;
+  parent = NULL;
+  is_leftchild = FALSE;
 
   while (1)
     {
-      int cmp = tree->key_compare (key, node->key, tree->key_compare_data);
+      gint cmp = tree->key_compare (key, node->key, tree->key_compare_data);
       
       if (cmp == 0)
         break;
@@ -607,166 +613,64 @@ g_tree_remove_internal (GTree         *tree,
         {
           if (!node->left_child)
             return FALSE;
-         
-         path[idx++] = node;
+
+         parent = node;
+          is_leftchild = TRUE;
          node = node->left;
         }
       else
         {
           if (!node->right_child)
             return FALSE;
-         
-         path[idx++] = node;
+
+         parent = node;
+          is_leftchild = FALSE;
          node = node->right;
         }
     }
 
-  /* the following code is almost equal to g_tree_remove_node,
-     except that we do not have to call g_tree_node_parent. */
-  balance = parent = path[--idx];
-  g_assert (!parent || parent->left == node || parent->right == node);
-  left_node = (parent && node == parent->left);
-
-  if (!node->left_child)
+  /* rotate the node down the tree, maintaining the heap property */
+  while (node->left_child || node->right_child)
     {
-      if (!node->right_child)
+      if (!node->left_child ||
+          (node->right_child &&
+           g_tree_priority (node->left) > g_tree_priority (node->right)))
         {
+          /* rotate the right child up */
           if (!parent)
-            tree->root = NULL;
-          else if (left_node)
-            {
-              parent->left_child = FALSE;
-              parent->left = node->left;
-              parent->balance += 1;
-            }
+            parent = tree->root = g_tree_node_rotate_left (node);
+          else if (is_leftchild)
+            parent = parent->left = g_tree_node_rotate_left (node);
           else
-            {
-              parent->right_child = FALSE;
-              parent->right = node->right;
-              parent->balance -= 1;
-            }
+            parent = parent->right = g_tree_node_rotate_left (node);
+          is_leftchild = TRUE;
         }
-      else /* node has a right child */
+      else
         {
-          GTreeNode *tmp = g_tree_node_next (node);
-         tmp->left = node->left;
-
+          /* rotate the left child up */
           if (!parent)
-            tree->root = node->right;
-          else if (left_node)
-            {
-              parent->left = node->right;
-              parent->balance += 1;
-            }
+            parent = tree->root = g_tree_node_rotate_right (node);
+          else if (is_leftchild)
+            parent = parent->left = g_tree_node_rotate_right (node);
           else
-            {
-              parent->right = node->right;
-              parent->balance -= 1;
-            }
+            parent = parent->right = g_tree_node_rotate_right (node);
+          is_leftchild = FALSE;
         }
     }
-  else /* node has a left child */
+
+  /* remove any pointers to the node in the treap */
+  if (!parent)
+    tree->root = NULL;
+  else if (is_leftchild)
     {
-      if (!node->right_child)
-        {
-          GTreeNode *tmp = g_tree_node_previous (node);
-          tmp->right = node->right;
-         
-          if (parent == NULL)
-            tree->root = node->left;
-          else if (left_node)
-            {
-              parent->left = node->left;
-              parent->balance += 1;
-            }
-          else
-            {
-              parent->right = node->left;
-              parent->balance -= 1;
-            }
-        }
-      else /* node has a both children (pant, pant!) */
-        {
-         GTreeNode *prev = node->left;
-         GTreeNode *next = node->right;
-         GTreeNode *nextp = node;
-         int old_idx = idx + 1;
-         idx++;
-         
-         /* path[idx] == parent */
-         /* find the immediately next node (and its parent) */
-         while (next->left_child)
-            {
-             path[++idx] = nextp = next;
-             next = next->left;
-            }
-         
-         path[old_idx] = next;
-         balance = path[idx];
-         
-         /* remove 'next' from the tree */
-         if (nextp != node)
-           {
-             if (next->right_child)
-               nextp->left = next->right;
-             else
-               nextp->left_child = FALSE;
-             nextp->balance += 1;
-             
-             next->right_child = TRUE;
-             next->right = node->right;
-           }
-         else
-           node->balance -= 1;
-           
-         /* set the prev to point to the right place */
-         while (prev->right_child)
-           prev = prev->right;
-         prev->right = next;
-           
-         /* prepare 'next' to replace 'node' */
-         next->left_child = TRUE;
-         next->left = node->left;
-         next->balance = node->balance;
-         
-         if (!parent)
-           tree->root = next;
-         else if (left_node)
-           parent->left = next;
-         else
-           parent->right = next;
-        }
+      parent->left_child = FALSE;
+      parent->left = node->left;
+    }
+  else
+    {
+      parent->right_child = FALSE;
+      parent->right = node->right;
     }
-  
-  /* restore balance */
-  if (balance)
-    while (1)
-      {
-       GTreeNode *bparent = path[--idx];
-       g_assert (!bparent || bparent->left == balance || bparent->right == balance);
-       left_node = (bparent && balance == bparent->left);
-                             
-       if(balance->balance < -1 || balance->balance > 1)
-         {
-           balance = g_tree_node_balance (balance);
-           if (!bparent)
-             tree->root = balance;
-           else if (left_node)
-             bparent->left = balance;
-           else
-             bparent->right = balance;
-         }
-       
-       if (balance->balance != 0 || !bparent)
-         break;
-       
-       if (left_node)
-         bparent->balance += 1;
-       else
-         bparent->balance -= 1;
-       
-       balance = bparent;
-      }
   
   if (!steal)
     {
@@ -964,6 +868,16 @@ g_tree_search (GTree         *tree,
     return NULL;
 }
 
+static gint
+g_tree_node_height(GTreeNode *node)
+{
+  gint l = 0, r = 0;
+  if (node == NULL) return 0;
+  if (node->left_child) l = g_tree_node_height (node->left);
+  if (node->right_child) r = g_tree_node_height (node->right);
+  return 1 + MAX(l, r);
+}
+
 /**
  * g_tree_height:
  * @tree: a #GTree.
@@ -979,26 +893,9 @@ g_tree_search (GTree         *tree,
 gint
 g_tree_height (GTree *tree)
 {
-  GTreeNode *node;
-  gint height;
-
   g_return_val_if_fail (tree != NULL, 0);
 
-  if (!tree->root)
-    return 0;
-
-  height = 0;
-  node = tree->root;
-
-  while (1)
-    {
-      height += 1 + MAX(node->balance, 0);
-
-      if (!node->left_child)
-       return height;
-      
-      node = node->left;
-    }
+  return g_tree_node_height (tree->root);
 }
 
 /**
@@ -1017,25 +914,6 @@ g_tree_nnodes (GTree *tree)
   return tree->nnodes;
 }
 
-static GTreeNode*
-g_tree_node_balance (GTreeNode *node)
-{
-  if (node->balance < -1)
-    {
-      if (node->left->balance > 0)
-       node->left = g_tree_node_rotate_left (node->left);
-      node = g_tree_node_rotate_right (node);
-    }
-  else if (node->balance > 1)
-    {
-      if (node->right->balance < 0)
-       node->right = g_tree_node_rotate_right (node->right);
-      node = g_tree_node_rotate_left (node);
-    }
-
-  return node;
-}
-
 static GTreeNode *
 g_tree_find_node (GTree        *tree,
                  gconstpointer key)
@@ -1174,13 +1052,14 @@ static GTreeNode*
 g_tree_node_rotate_left (GTreeNode *node)
 {
   GTreeNode *right;
-  gint a_bal;
-  gint b_bal;
 
   right = node->right;
 
   if (right->left_child)
-    node->right = right->left;
+    {
+      node->right = right->left;
+      node->right->parent = node;
+    }
   else
     {
       node->right_child = FALSE;
@@ -1188,26 +1067,8 @@ g_tree_node_rotate_left (GTreeNode *node)
       right->left_child = TRUE;
     }
   right->left = node;
-
-  a_bal = node->balance;
-  b_bal = right->balance;
-
-  if (b_bal <= 0)
-    {
-      if (a_bal >= 1)
-       right->balance = b_bal - 1;
-      else
-       right->balance = a_bal + b_bal - 2;
-      node->balance = a_bal - 1;
-    }
-  else
-    {
-      if (a_bal <= b_bal)
-       right->balance = a_bal - 2;
-      else
-       right->balance = b_bal - 1;
-      node->balance = a_bal - b_bal - 1;
-    }
+  right->parent = node->parent;
+  node->parent = right;
 
   return right;
 }
@@ -1216,13 +1077,14 @@ static GTreeNode*
 g_tree_node_rotate_right (GTreeNode *node)
 {
   GTreeNode *left;
-  gint a_bal;
-  gint b_bal;
 
   left = node->left;
 
   if (left->right_child)
-    node->left = left->right;
+    {
+      node->left = left->right;
+      node->left->parent = node;
+    }
   else
     {
       node->left_child = FALSE;
@@ -1230,60 +1092,18 @@ g_tree_node_rotate_right (GTreeNode *node)
       left->right_child = TRUE;
     }
   left->right = node;
-
-  a_bal = node->balance;
-  b_bal = left->balance;
-
-  if (b_bal <= 0)
-    {
-      if (b_bal > a_bal)
-       left->balance = b_bal + 1;
-      else
-       left->balance = a_bal + 2;
-      node->balance = a_bal - b_bal + 1;
-    }
-  else
-    {
-      if (a_bal <= -1)
-       left->balance = b_bal + 1;
-      else
-       left->balance = a_bal + b_bal + 2;
-      node->balance = a_bal + 1;
-    }
+  left->parent = node->parent;
+  node->parent = left;
 
   return left;
 }
 
 #ifdef G_TREE_DEBUG
-static gint
-g_tree_node_height (GTreeNode *node)
-{
-  gint left_height;
-  gint right_height;
-
-  if (node)
-    {
-      left_height = 0;
-      right_height = 0;
-
-      if (node->left_child)
-       left_height = g_tree_node_height (node->left);
-
-      if (node->right_child)
-       right_height = g_tree_node_height (node->right);
-
-      return MAX (left_height, right_height) + 1;
-    }
-
-  return 0;
-}
-
 static void
 g_tree_node_check (GTreeNode *node)
 {
   gint left_height;
   gint right_height;
-  gint balance;
   GTreeNode *tmp;
 
   if (node)
@@ -1308,9 +1128,6 @@ g_tree_node_check (GTreeNode *node)
       if (node->right_child)
        right_height = g_tree_node_height (node->right);
       
-      balance = right_height - left_height;
-      g_assert (balance == node->balance);
-      
       if (node->left_child)
        g_tree_node_check (node->left);
       if (node->right_child)
index c5cb105..bd7f13e 100644 (file)
@@ -64,6 +64,49 @@ my_value_destroy (gpointer value)
   destroyed_value = value;
 }
 
+static gint preorder_i;
+static char *preorder;
+
+static gint
+traverse_preorder (gpointer key,
+                   gpointer value,
+                   gpointer data)
+{
+  char *ch = key;
+  preorder[preorder_i++] = *ch;
+  return FALSE;
+}
+
+static gint
+calc_height_recur(char *inorder, char *preorder, int l, int r)
+{
+  gint i, parentpos_in, left, right;
+  char parent;
+
+  if (r < l) return 0;
+
+  parent = preorder[l++];
+  parentpos_in = strchr(inorder, parent);
+
+  /* find the position of the right child in the preorder string */
+  right = 0;
+  for (i = l; i <= r; ++i)
+    if (strchr(inorder, preorder[i]) > parentpos_in) {
+      right = calc_height_recur(inorder, preorder, i, r);
+      r = i-1; /* exlcude the right subtree when looking at the left */
+      break;
+    }
+  left = calc_height_recur(inorder, preorder, l, r);
+  return 1 + MAX(left, right);
+}
+
+static gint
+calc_height(char *inorder, char *preorder, int n)
+{
+  return calc_height_recur(inorder, preorder, 0, n-1);
+}
+
+static gint traverse_num = 0;
 static gint
 my_traverse (gpointer key,
             gpointer value,
@@ -71,6 +114,7 @@ my_traverse (gpointer key,
 {
   char *ch = key;
   g_assert ((*ch) > 0);
+  ++traverse_num;
   return FALSE;
 }
 
@@ -112,13 +156,25 @@ main (int   argc,
 
   tree = g_tree_new (my_compare);
 
-  for (i = 0; chars[i]; i++)
+  for (i = 0; chars[i]; i++) {
     g_tree_insert (tree, &chars[i], &chars[i]);
+  }
+  g_assert(i == g_tree_nnodes(tree));
 
+  traverse_num = 0;
   g_tree_foreach (tree, my_traverse, NULL);
+  g_assert(traverse_num == g_tree_nnodes(tree));
 
   g_assert (g_tree_nnodes (tree) == strlen (chars));
-  g_assert (g_tree_height (tree) == 6);
+  /*g_assert (g_tree_height (tree) == 6);*/
+
+  preorder = g_malloc(strlen(chars)+1);
+  preorder[strlen(chars)] = 0;
+  preorder_i = 0;
+  g_tree_traverse (tree, traverse_preorder, G_PRE_ORDER, NULL);
+  g_assert(traverse_num == g_tree_nnodes(tree));
+  g_assert(calc_height(chars, preorder, strlen(chars)) == g_tree_height(tree));
+  g_free(preorder);
   
   p = chars;
   g_tree_foreach (tree, check_order, &p);
@@ -133,10 +189,12 @@ main (int   argc,
   removed = g_tree_remove (tree, &c);
   g_assert (removed == FALSE);
 
+  traverse_num = 0;
   g_tree_foreach (tree, my_traverse, NULL);
+  g_assert(traverse_num == g_tree_nnodes(tree));
 
   g_assert (g_tree_nnodes (tree) == strlen (chars2));
-  g_assert (g_tree_height (tree) == 6);
+  /*g_assert (g_tree_height (tree) == 6);*/
 
   p = chars2;
   g_tree_foreach (tree, check_order, &p);