Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  rbtree.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * rbtree.c
 *   implementation for PostgreSQL generic Red-Black binary tree package
 *   Adopted from http://algolist.manual.ru/ds/rbtree.php
 *
 * This code comes from Thomas Niemann's "Sorting and Searching Algorithms:
 * a Cookbook".
 *
 * See http://www.cs.auckland.ac.nz/software/AlgAnim/niemann/s_man.htm for
 * license terms: "Source code, when part of a software project, may be used
 * freely without reference to the author."
 *
 * Red-black trees are a type of balanced binary tree wherein (1) any child of
 * a red node is always black, and (2) every path from root to leaf traverses
 * an equal number of black nodes.  From these properties, it follows that the
 * longest path from root to leaf is only about twice as long as the shortest,
 * so lookups are guaranteed to run in O(lg n) time.
 *
 * Copyright (c) 2009-2025, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *   src/backend/lib/rbtree.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "lib/rbtree.h"


/*
 * Colors of nodes (values of RBTNode.color)
 */

#define RBTBLACK (0)
#define RBTRED  (1)

/*
 * RBTree control structure
 */

struct RBTree
{
 RBTNode    *root;   /* root node, or RBTNIL if tree is empty */

 /* Remaining fields are constant after rbt_create */

 Size  node_size;  /* actual size of tree nodes */
 /* The caller-supplied manipulation functions */
 rbt_comparator comparator;
 rbt_combiner combiner;
 rbt_allocfunc allocfunc;
 rbt_freefunc freefunc;
 /* Passthrough arg passed to all manipulation functions */
 void    *arg;
};

/*
 * all leafs are sentinels, use customized NIL name to prevent
 * collision with system-wide constant NIL which is actually NULL
 */

#define RBTNIL (&sentinel)

static RBTNode sentinel =
{
 .color = RBTBLACK,.left = RBTNIL,.right = RBTNIL,.parent = NULL
};


/*
 * rbt_create: create an empty RBTree
 *
 * Arguments are:
 * node_size: actual size of tree nodes (> sizeof(RBTNode))
 * The manipulation functions:
 * comparator: compare two RBTNodes for less/equal/greater
 * combiner: merge an existing tree entry with a new one
 * allocfunc: allocate a new RBTNode
 * freefunc: free an old RBTNode
 * arg: passthrough pointer that will be passed to the manipulation functions
 *
 * Note that the combiner's righthand argument will be a "proposed" tree node,
 * ie the input to rbt_insert, in which the RBTNode fields themselves aren't
 * valid.  Similarly, either input to the comparator may be a "proposed" node.
 * This shouldn't matter since the functions aren't supposed to look at the
 * RBTNode fields, only the extra fields of the struct the RBTNode is embedded
 * in.
 *
 * The freefunc should just be pfree or equivalent; it should NOT attempt
 * to free any subsidiary data, because the node passed to it may not contain
 * valid data! freefunc can be NULL if caller doesn't require retail
 * space reclamation.
 *
 * The RBTree node is palloc'd in the caller's memory context.  Note that
 * all contents of the tree are actually allocated by the caller, not here.
 *
 * Since tree contents are managed by the caller, there is currently not
 * an explicit "destroy" operation; typically a tree would be freed by
 * resetting or deleting the memory context it's stored in.  You can pfree
 * the RBTree node if you feel the urge.
 */

RBTree *
rbt_create(Size node_size,
     rbt_comparator comparator,
     rbt_combiner combiner,
     rbt_allocfunc allocfunc,
     rbt_freefunc freefunc,
     void *arg)
{
 RBTree    *tree = (RBTree *) palloc(sizeof(RBTree));

 Assert(node_size > sizeof(RBTNode));

 tree->root = RBTNIL;
 tree->node_size = node_size;
 tree->comparator = comparator;
 tree->combiner = combiner;
 tree->allocfunc = allocfunc;
 tree->freefunc = freefunc;

 tree->arg = arg;

 return tree;
}

/* Copy the additional data fields from one RBTNode to another */
static inline void
rbt_copy_data(RBTree *rbt, RBTNode *dest, const RBTNode *src)
{
 memcpy(dest + 1, src + 1, rbt->node_size - sizeof(RBTNode));
}

/**********************************************************************
 *        Search           *
 **********************************************************************/


/*
 * rbt_find: search for a value in an RBTree
 *
 * data represents the value to try to find.  Its RBTNode fields need not
 * be valid, it's the extra data in the larger struct that is of interest.
 *
 * Returns the matching tree entry, or NULL if no match is found.
 */

RBTNode *
rbt_find(RBTree *rbt, const RBTNode *data)
{
 RBTNode    *node = rbt->root;

 while (node != RBTNIL)
 {
  int   cmp = rbt->comparator(data, node, rbt->arg);

  if (cmp == 0)
   return node;
  else if (cmp < 0)
   node = node->left;
  else
   node = node->right;
 }

 return NULL;
}

/*
 * rbt_find_great: search for a greater value in an RBTree
 *
 * If equal_match is true, this will be a great or equal search.
 *
 * Returns the matching tree entry, or NULL if no match is found.
 */

RBTNode *
rbt_find_great(RBTree *rbt, const RBTNode *data, bool equal_match)
{
 RBTNode    *node = rbt->root;
 RBTNode    *greater = NULL;

 while (node != RBTNIL)
 {
  int   cmp = rbt->comparator(data, node, rbt->arg);

  if (equal_match && cmp == 0)
   return node;
  else if (cmp < 0)
  {
   greater = node;
   node = node->left;
  }
  else
   node = node->right;
 }

 return greater;
}

/*
 * rbt_find_less: search for a lesser value in an RBTree
 *
 * If equal_match is true, this will be a less or equal search.
 *
 * Returns the matching tree entry, or NULL if no match is found.
 */

RBTNode *
rbt_find_less(RBTree *rbt, const RBTNode *data, bool equal_match)
{
 RBTNode    *node = rbt->root;
 RBTNode    *lesser = NULL;

 while (node != RBTNIL)
 {
  int   cmp = rbt->comparator(data, node, rbt->arg);

  if (equal_match && cmp == 0)
   return node;
  else if (cmp > 0)
  {
   lesser = node;
   node = node->right;
  }
  else
   node = node->left;
 }

 return lesser;
}

/*
 * rbt_leftmost: fetch the leftmost (smallest-valued) tree node.
 * Returns NULL if tree is empty.
 *
 * Note: in the original implementation this included an unlink step, but
 * that's a bit awkward.  Just call rbt_delete on the result if that's what
 * you want.
 */

RBTNode *
rbt_leftmost(RBTree *rbt)
{
 RBTNode    *node = rbt->root;
 RBTNode    *leftmost = rbt->root;

 while (node != RBTNIL)
 {
  leftmost = node;
  node = node->left;
 }

 if (leftmost != RBTNIL)
  return leftmost;

 return NULL;
}

/**********************************************************************
 *         Insertion          *
 **********************************************************************/


/*
 * Rotate node x to left.
 *
 * x's right child takes its place in the tree, and x becomes the left
 * child of that node.
 */

static void
rbt_rotate_left(RBTree *rbt, RBTNode *x)
{
 RBTNode    *y = x->right;

 /* establish x->right link */
 x->right = y->left;
 if (y->left != RBTNIL)
  y->left->parent = x;

 /* establish y->parent link */
 if (y != RBTNIL)
  y->parent = x->parent;
 if (x->parent)
 {
  if (x == x->parent->left)
   x->parent->left = y;
  else
   x->parent->right = y;
 }
 else
 {
  rbt->root = y;
 }

 /* link x and y */
 y->left = x;
 if (x != RBTNIL)
  x->parent = y;
}

/*
 * Rotate node x to right.
 *
 * x's left right child takes its place in the tree, and x becomes the right
 * child of that node.
 */

static void
rbt_rotate_right(RBTree *rbt, RBTNode *x)
{
 RBTNode    *y = x->left;

 /* establish x->left link */
 x->left = y->right;
 if (y->right != RBTNIL)
  y->right->parent = x;

 /* establish y->parent link */
 if (y != RBTNIL)
  y->parent = x->parent;
 if (x->parent)
 {
  if (x == x->parent->right)
   x->parent->right = y;
  else
   x->parent->left = y;
 }
 else
 {
  rbt->root = y;
 }

 /* link x and y */
 y->right = x;
 if (x != RBTNIL)
  x->parent = y;
}

/*
 * Maintain Red-Black tree balance after inserting node x.
 *
 * The newly inserted node is always initially marked red.  That may lead to
 * a situation where a red node has a red child, which is prohibited.  We can
 * always fix the problem by a series of color changes and/or "rotations",
 * which move the problem progressively higher up in the tree.  If one of the
 * two red nodes is the root, we can always fix the problem by changing the
 * root from red to black.
 *
 * (This does not work lower down in the tree because we must also maintain
 * the invariant that every leaf has equal black-height.)
 */

static void
rbt_insert_fixup(RBTree *rbt, RBTNode *x)
{
 /*
  * x is always a red node.  Initially, it is the newly inserted node. Each
  * iteration of this loop moves it higher up in the tree.
 */

 while (x != rbt->root && x->parent->color == RBTRED)
 {
  /*
   * x and x->parent are both red.  Fix depends on whether x->parent is
   * a left or right child.  In either case, we define y to be the
   * "uncle" of x, that is, the other child of x's grandparent.
   *
   * If the uncle is red, we flip the grandparent to red and its two
   * children to black.  Then we loop around again to check whether the
   * grandparent still has a problem.
   *
   * If the uncle is black, we will perform one or two "rotations" to
   * balance the tree.  Either x or x->parent will take the
   * grandparent's position in the tree and recolored black, and the
   * original grandparent will be recolored red and become a child of
   * that node. This always leaves us with a valid red-black tree, so
   * the loop will terminate.
 */

  if (x->parent == x->parent->parent->left)
  {
   RBTNode    *y = x->parent->parent->right;

   if (y->color == RBTRED)
   {
    /* uncle is RBTRED */
    x->parent->color = RBTBLACK;
    y->color = RBTBLACK;
    x->parent->parent->color = RBTRED;

    x = x->parent->parent;
   }
   else
   {
    /* uncle is RBTBLACK */
    if (x == x->parent->right)
    {
     /* make x a left child */
     x = x->parent;
     rbt_rotate_left(rbt, x);
    }

    /* recolor and rotate */
    x->parent->color = RBTBLACK;
    x->parent->parent->color = RBTRED;

    rbt_rotate_right(rbt, x->parent->parent);
   }
  }
  else
  {
   /* mirror image of above code */
   RBTNode    *y = x->parent->parent->left;

   if (y->color == RBTRED)
   {
    /* uncle is RBTRED */
    x->parent->color = RBTBLACK;
    y->color = RBTBLACK;
    x->parent->parent->color = RBTRED;

    x = x->parent->parent;
   }
   else
   {
    /* uncle is RBTBLACK */
    if (x == x->parent->left)
    {
     x = x->parent;
     rbt_rotate_right(rbt, x);
    }
    x->parent->color = RBTBLACK;
    x->parent->parent->color = RBTRED;

    rbt_rotate_left(rbt, x->parent->parent);
   }
  }
 }

 /*
  * The root may already have been black; if not, the black-height of every
  * node in the tree increases by one.
 */

 rbt->root->color = RBTBLACK;
}

/*
 * rbt_insert: insert a new value into the tree.
 *
 * data represents the value to insert.  Its RBTNode fields need not
 * be valid, it's the extra data in the larger struct that is of interest.
 *
 * If the value represented by "data" is not present in the tree, then
 * we copy "data" into a new tree entry and return that node, setting *isNew
 * to true.
 *
 * If the value represented by "data" is already present, then we call the
 * combiner function to merge data into the existing node, and return the
 * existing node, setting *isNew to false.
 *
 * "data" is unmodified in either case; it's typically just a local
 * variable in the caller.
 */

RBTNode *
rbt_insert(RBTree *rbt, const RBTNode *data, bool *isNew)
{
 RBTNode    *current,
      *parent,
      *x;
 int   cmp;

 /* find where node belongs */
 current = rbt->root;
 parent = NULL;
 cmp = 0;     /* just to prevent compiler warning */

 while (current != RBTNIL)
 {
  cmp = rbt->comparator(data, current, rbt->arg);
  if (cmp == 0)
  {
   /*
    * Found node with given key.  Apply combiner.
 */

   rbt->combiner(current, data, rbt->arg);
   *isNew = false;
   return current;
  }
  parent = current;
  current = (cmp < 0) ? current->left : current->right;
 }

 /*
  * Value is not present, so create a new node containing data.
 */

 *isNew = true;

 x = rbt->allocfunc(rbt->arg);

 x->color = RBTRED;

 x->left = RBTNIL;
 x->right = RBTNIL;
 x->parent = parent;
 rbt_copy_data(rbt, x, data);

 /* insert node in tree */
 if (parent)
 {
  if (cmp < 0)
   parent->left = x;
  else
   parent->right = x;
 }
 else
 {
  rbt->root = x;
 }

 rbt_insert_fixup(rbt, x);

 return x;
}

/**********************************************************************
 *       Deletion          *
 **********************************************************************/


/*
 * Maintain Red-Black tree balance after deleting a black node.
 */

static void
rbt_delete_fixup(RBTree *rbt, RBTNode *x)
{
 /*
  * x is always a black node.  Initially, it is the former child of the
  * deleted node.  Each iteration of this loop moves it higher up in the
  * tree.
 */

 while (x != rbt->root && x->color == RBTBLACK)
 {
  /*
   * Left and right cases are symmetric.  Any nodes that are children of
   * x have a black-height one less than the remainder of the nodes in
   * the tree.  We rotate and recolor nodes to move the problem up the
   * tree: at some stage we'll either fix the problem, or reach the root
   * (where the black-height is allowed to decrease).
 */

  if (x == x->parent->left)
  {
   RBTNode    *w = x->parent->right;

   if (w->color == RBTRED)
   {
    w->color = RBTBLACK;
    x->parent->color = RBTRED;

    rbt_rotate_left(rbt, x->parent);
    w = x->parent->right;
   }

   if (w->left->color == RBTBLACK && w->right->color == RBTBLACK)
   {
    w->color = RBTRED;

    x = x->parent;
   }
   else
   {
    if (w->right->color == RBTBLACK)
    {
     w->left->color = RBTBLACK;
     w->color = RBTRED;

     rbt_rotate_right(rbt, w);
     w = x->parent->right;
    }
    w->color = x->parent->color;
    x->parent->color = RBTBLACK;
    w->right->color = RBTBLACK;

    rbt_rotate_left(rbt, x->parent);
    x = rbt->root; /* Arrange for loop to terminate. */
   }
  }
  else
  {
   RBTNode    *w = x->parent->left;

   if (w->color == RBTRED)
   {
    w->color = RBTBLACK;
    x->parent->color = RBTRED;

    rbt_rotate_right(rbt, x->parent);
    w = x->parent->left;
   }

   if (w->right->color == RBTBLACK && w->left->color == RBTBLACK)
   {
    w->color = RBTRED;

    x = x->parent;
   }
   else
   {
    if (w->left->color == RBTBLACK)
    {
     w->right->color = RBTBLACK;
     w->color = RBTRED;

     rbt_rotate_left(rbt, w);
     w = x->parent->left;
    }
    w->color = x->parent->color;
    x->parent->color = RBTBLACK;
    w->left->color = RBTBLACK;

    rbt_rotate_right(rbt, x->parent);
    x = rbt->root; /* Arrange for loop to terminate. */
   }
  }
 }
 x->color = RBTBLACK;
}

/*
 * Delete node z from tree.
 */

static void
rbt_delete_node(RBTree *rbt, RBTNode *z)
{
 RBTNode    *x,
      *y;

 /* This is just paranoia: we should only get called on a valid node */
 if (!z || z == RBTNIL)
  return;

 /*
  * y is the node that will actually be removed from the tree.  This will
  * be z if z has fewer than two children, or the tree successor of z
  * otherwise.
 */

 if (z->left == RBTNIL || z->right == RBTNIL)
 {
  /* y has a RBTNIL node as a child */
  y = z;
 }
 else
 {
  /* find tree successor */
  y = z->right;
  while (y->left != RBTNIL)
   y = y->left;
 }

 /* x is y's only child */
 if (y->left != RBTNIL)
  x = y->left;
 else
  x = y->right;

 /* Remove y from the tree. */
 x->parent = y->parent;
 if (y->parent)
 {
  if (y == y->parent->left)
   y->parent->left = x;
  else
   y->parent->right = x;
 }
 else
 {
  rbt->root = x;
 }

 /*
  * If we removed the tree successor of z rather than z itself, then move
  * the data for the removed node to the one we were supposed to remove.
 */

 if (y != z)
  rbt_copy_data(rbt, z, y);

 /*
  * Removing a black node might make some paths from root to leaf contain
  * fewer black nodes than others, or it might make two red nodes adjacent.
 */

 if (y->color == RBTBLACK)
  rbt_delete_fixup(rbt, x);

 /* Now we can recycle the y node */
 if (rbt->freefunc)
  rbt->freefunc(y, rbt->arg);
}

/*
 * rbt_delete: remove the given tree entry
 *
 * "node" must have previously been found via rbt_find or rbt_leftmost.
 * It is caller's responsibility to free any subsidiary data attached
 * to the node before calling rbt_delete.  (Do *not* try to push that
 * responsibility off to the freefunc, as some other physical node
 * may be the one actually freed!)
 */

void
rbt_delete(RBTree *rbt, RBTNode *node)
{
 rbt_delete_node(rbt, node);
}

/**********************************************************************
 *        Traverse           *
 **********************************************************************/


static RBTNode *
rbt_left_right_iterator(RBTreeIterator *iter)
{
 if (iter->last_visited == NULL)
 {
  iter->last_visited = iter->rbt->root;
  while (iter->last_visited->left != RBTNIL)
   iter->last_visited = iter->last_visited->left;

  return iter->last_visited;
 }

 if (iter->last_visited->right != RBTNIL)
 {
  iter->last_visited = iter->last_visited->right;
  while (iter->last_visited->left != RBTNIL)
   iter->last_visited = iter->last_visited->left;

  return iter->last_visited;
 }

 for (;;)
 {
  RBTNode    *came_from = iter->last_visited;

  iter->last_visited = iter->last_visited->parent;
  if (iter->last_visited == NULL)
  {
   iter->is_over = true;
   break;
  }

  if (iter->last_visited->left == came_from)
   break;    /* came from left sub-tree, return current
 * node */


  /* else - came from right sub-tree, continue to move up */
 }

 return iter->last_visited;
}

static RBTNode *
rbt_right_left_iterator(RBTreeIterator *iter)
{
 if (iter->last_visited == NULL)
 {
  iter->last_visited = iter->rbt->root;
  while (iter->last_visited->right != RBTNIL)
   iter->last_visited = iter->last_visited->right;

  return iter->last_visited;
 }

 if (iter->last_visited->left != RBTNIL)
 {
  iter->last_visited = iter->last_visited->left;
  while (iter->last_visited->right != RBTNIL)
   iter->last_visited = iter->last_visited->right;

  return iter->last_visited;
 }

 for (;;)
 {
  RBTNode    *came_from = iter->last_visited;

  iter->last_visited = iter->last_visited->parent;
  if (iter->last_visited == NULL)
  {
   iter->is_over = true;
   break;
  }

  if (iter->last_visited->right == came_from)
   break;    /* came from right sub-tree, return current
 * node */


  /* else - came from left sub-tree, continue to move up */
 }

 return iter->last_visited;
}

/*
 * rbt_begin_iterate: prepare to traverse the tree in any of several orders
 *
 * After calling rbt_begin_iterate, call rbt_iterate repeatedly until it
 * returns NULL or the traversal stops being of interest.
 *
 * If the tree is changed during traversal, results of further calls to
 * rbt_iterate are unspecified.  Multiple concurrent iterators on the same
 * tree are allowed.
 *
 * The iterator state is stored in the 'iter' struct.  The caller should
 * treat it as an opaque struct.
 */

void
rbt_begin_iterate(RBTree *rbt, RBTOrderControl ctrl, RBTreeIterator *iter)
{
 /* Common initialization for all traversal orders */
 iter->rbt = rbt;
 iter->last_visited = NULL;
 iter->is_over = (rbt->root == RBTNIL);

 switch (ctrl)
 {
  case LeftRightWalk:  /* visit left, then self, then right */
   iter->iterate = rbt_left_right_iterator;
   break;
  case RightLeftWalk:  /* visit right, then self, then left */
   iter->iterate = rbt_right_left_iterator;
   break;
  default:
   elog(ERROR, "unrecognized rbtree iteration order: %d", ctrl);
 }
}

/*
 * rbt_iterate: return the next node in traversal order, or NULL if no more
 */

RBTNode *
rbt_iterate(RBTreeIterator *iter)
{
 if (iter->is_over)
  return NULL;

 return iter->iterate(iter);
}

Messung V0.5 in Prozent
C=98 H=94 G=95

¤ Dauer der Verarbeitung: 0.6 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142