Algorithmen und Datenstrukturen in Chome Algorithmen und Datenstrukturen in C: Rot-Schwarz-Bäume Prof. Dr. Uwe Schmidt FH Wedel

Rot-Schwarz-Bäume

weiter

weiter

Implementierung von Rot-Schwarz-Bäumen

Rot-Schwarz-Bäume
sind Binärbäume, die beim Einfügen "on the fly" ausbalanciert werden.
 
Zusätzlich zu Binärbäumen wird in jedem Knoten eine Farbe gespeichert, und zwar gibt es rote und schwarze Knoten. Diese Zusatzinformation wird nur beim Einfügen und Löschen von Elementen berücksichtigt. Das Suchen läuft wie bei einfachen binären Bäumen.
 
Ein Rot-Schwarz-Baum muss folgende Balance-Invarianten erfüllen:
  1. Kein roter Knoten besitzt einen roten Kindknoten.
  2. Jeder Pfad von der Wurzel zu einem leeren Baum enthält die gleiche Anzahl von schwarzen Knoten.
Leere Bäume werden immer als schwarz eingefärbt betrachtet.
 
Diese beiden Invarianten garantieren, dass der längste mögliche Pfad in einem Rot-Schwarz-Baum, einer mit abwechselnd schwarzen und roten Knoten, höchstens doppelt so lang sein kann, wie der kürzeste, der nur schwarze Knoten enthält.
 
Daraus folgt, dass die maximale Tiefe eines Baumes mit n Elementen höchstens 2 * log2 (n + 1) sein kann.
 
Beim Einfügen wird der Baum auf dem Weg von der Einfügestelle zurück zur Wurzel an den Stellen ausbalanciert, an denen zwei rote Knoten entstanden sind. Neu eingefügte Knoten werden immer als rot markiert.
 
Das Löschen läuft prinzipiell genauso ab wie das Einfügen, nur können dabei doppelt schwarze Knoten entstehen, d.h. schwarze Knoten, die beim Zählen das Gewicht 2 haben. Diese werden durch lokale Umstrukturierung auf dem Rückweg zur Wurzel wieder eliminiert. Die Löschroutinen werden in diesem Beispiel nicht behandelt.
 
Die Typdefinition ist eine Erweiterung der für binäre Bäume: Pro Knoten wird zusätzlich eine Farbe gespeichert.
 
Diese Beispiel-Implementierung lässt noch Speilraum für lokale Optimierungen. Einmal können die einfachen Funktionen, insbesondere die Hilfsfunktionen, zum Teil durch Makros einkopiert werden. Weiter kann das Ausbalancieren vebessert werden: Wenn in den linken Teilbaum eingefügt wird, muss auch nur der linke Baum auf Ausgewogenheit getestet werden. Analoges gilt für den rechten Teilbaum. Die kritischen Funktionen erkennt man mit einem Profiling-Lauf, wie im Beispiel für die Skew-Heaps realisiert.
weiter

weiter

Die Schnittstelle: Element.h

   1#ifndef ELEMENT_H__
   2#define ELEMENT_H__ 1
   3
   4/* $Id: Element.h,v 1.1 2004/06/02 20:02:32 uwe Exp $ */
   5
   6typedef int Element;
   7
   8extern int compare(Element e1,
   9                   Element e2);
  10
  11#endif
weiter

weiter

Die Implementierung: Element.c

   1static char version[] =
   2  "$Id: Element.c,v 1.1 2004/06/02 20:02:32 uwe Exp $";
   3
   4#include "Element.h"
   5
   6int
   7compare(Element e1Element e2)
   8{
   9  return (e1 >= e2) - (e2 >= e1);
  10}
weiter

weiter

Die Schnittstelle: Set.h

   1#ifndef SET_H__
   2#define SET_H__
   3
   4/* $Id: Set.h,v 1.1 2004/06/02 20:02:32 uwe Exp $ */
   5
   6/*--------------------*/
   7
   8#include "Element.h"
   9
  10typedef struct Node *Set;
  11
  12struct Node
  13{
  14  enum
  15  { REDBLACK } color;
  16  Element info;
  17  Set l;
  18  Set r;
  19};
  20
  21/*--------------------*/
  22
  23extern Set mkEmptySet(void);
  24extern int isEmptySet(Set s);
  25
  26extern Set mkOneElemSet(Element e);
  27extern Set insertElem(Element eSet s);
  28
  29extern int isInSet(Element eSet s);
  30
  31extern unsigned int card(Set s);
  32
  33extern unsigned int maxPathLength(Set
  34                                  s);
  35extern unsigned int minPathLength(Set
  36                                  s);
  37
  38extern int invSetAsRedBlackTree(Set s);
  39
  40/*--------------------*/
  41
  42#endif
weiter

weiter

Die Implementierung: Set.c

   1static char version[] =
   2  "$Id: Set.c,v 1.3 2006/01/31 18:27:51 uwe Exp $";
   3
   4#include "Set.h"
   5
   6#include <stdlib.h>
   7#include <stdio.h>
   8#include <assert.h>
   9
  10/*--------------------*/
  11
  12/* special empty node, marked as black */
  13
  14static struct Node finalNode = { BLACK }
  15
  16Set
  17mkEmptySet(void)
  18{
  19  return &finalNode;
  20}
  21
  22/* local optimization */
  23
  24#define mkEmptySet() (&finalNode)
  25
  26/*--------------------*/
  27
  28int
  29isEmptySet(Set s)
  30{
  31  return s == &finalNode;
  32}
  33
  34/* local optimization */
  35
  36#define isEmptySet(s) ((s) == &finalNode)
  37
  38/*--------------------*/
  39
  40/* auxiliary function: file scope */
  41
  42static Set
  43mkNewRedNode(Element e)
  44{
  45  Set res = malloc(sizeof(*res));
  46
  47  if (!res)
  48    {
  49      perror
  50        ("mkNewNode: malloc failed");
  51      exit(1);
  52    }
  53
  54  res->color = RED;             /* every new node is red */
  55  res->info = e;
  56  res->l = mkEmptySet();
  57  res->r = mkEmptySet();
  58
  59  return res;
  60}
  61
  62/*--------------------*/
  63
  64Set
  65mkOneElemSet(Element e)
  66{
  67  return insertElem(emkEmptySet());
  68}
  69
  70/*--------------------*/
  71
  72int
  73isInSet(Element eSet s)
  74{
  75  if (isEmptySet(s))
  76    return 0;
  77
  78  switch (compare(es->info))
  79    {
  80    case -1:
  81      return isInSet(es->l);
  82    case 0:
  83      return 1;
  84    case +1:
  85      return isInSet(es->r);
  86    }
  87
  88  assert(0);
  89  return 0;
  90}
  91
  92/*--------------------*/
  93
  94/* color predicates */
  95
  96#define isBlackNode(s) ((s)->color == BLACK)
  97#define isRedNode(s) (! isBlackNode(s))
  98
  99static int
 100hasRedChild(Set s)
 101{
 102  return
 103    ! isEmptySet(s)
 104    && (isRedNode(s->l)
 105        ||
 106        isRedNode(s->r));
 107}
 108
 109/*--------------------*/
 110
 111/* reorganize tree */
 112
 113static Set
 114balanceTrees(Set xSet ySet z,
 115             Set bSet c)
 116{
 117  x->r = b;
 118  z->l = c;
 119
 120  y->l = x;
 121  y->r = z;
 122
 123  x->color = BLACK;
 124  z->color = BLACK;
 125  y->color = RED;
 126
 127  return y;
 128}
 129
 130/* check invariant */
 131
 132/* check balance in left subtree */
 133
 134static Set
 135checkBalanceLeft(Set s)
 136{
 137  assert(!isEmptySet(s));
 138
 139  /* no balancing of trees with red root */
 140
 141  if (isRedNode(s))
 142    return s;
 143
 144  if (isRedNode(s->l)
 145      && isRedNode(s->l->l))
 146    return balanceTrees(s->l->l,
 147                        s->l,
 148                        s,
 149                        s->l->l->r,
 150                        s->l->r);
 151
 152  if (isRedNode(s->l)
 153      && isRedNode(s->l->r))
 154    return balanceTrees(s->l,
 155                        s->l->r,
 156                        s,
 157                        s->l->r->l,
 158                        s->l->r->r);
 159
 160  return s;
 161}
 162
 163/* check balance in right subtree */
 164
 165static Set
 166checkBalanceRight(Set s)
 167{
 168  assert(!isEmptySet(s));
 169
 170  /* no balancing of trees with red root */
 171
 172  if (isRedNode(s))
 173    return s;
 174
 175  if (isRedNode(s->r)
 176      && isRedNode(s->r->l))
 177    return balanceTrees(s,
 178                        s->r->l,
 179                        s->r,
 180                        s->r->l->l,
 181                        s->r->l->r);
 182
 183  if (isRedNode(s->r)
 184      && isRedNode(s->r->r))
 185    return balanceTrees(s,
 186                        s->r,
 187                        s->r->r,
 188                        s->r->l,
 189                        s->r->r->l);
 190
 191  return s;
 192}
 193
 194/*--------------------*/
 195
 196static Set
 197insertElem1(Element eSet s)
 198{
 199  if (isEmptySet(s))
 200    return mkNewRedNode(e);
 201
 202  switch (compare(es->info))
 203    {
 204    case -1:
 205      s->l = insertElem1(es->l);
 206
 207      /* invariant is checked with left subtree */
 208      return checkBalanceLeft(s);
 209
 210    case 0:
 211      break;
 212
 213    case +1:
 214      s->r = insertElem1(es->r);
 215
 216      /* invariant is checked with right subtree */
 217      return checkBalanceRight(s);
 218    }
 219
 220  return s;
 221}
 222
 223/*--------------------*/
 224
 225Set
 226insertElem(Element eSet s)
 227{
 228  Set res = insertElem1(es);
 229
 230  /* the root is always black */
 231
 232  res->color = BLACK;
 233
 234  assert(invSetAsRedBlackTree(res));
 235
 236  return res;
 237}
 238
 239/*--------------------*/
 240
 241static int
 242lessThan(Set sElement e)
 243{
 244  return
 245    isEmptySet(s)
 246    || (compare(s->infoe) == -1
 247        && lessThan(s->re));
 248
 249}
 250
 251/*--------------------*/
 252
 253static int
 254greaterThan(Set sElement e)
 255{
 256  return
 257    isEmptySet(s)
 258    || (
 259        compare(s->infoe) == +1
 260        &&
 261        greaterThan(s->le));
 262}
 263
 264/*--------------------*/
 265
 266static int
 267invSetAsBinTree(Set s)
 268{
 269  return
 270    isEmptySet(s)
 271    || (
 272        lessThan(s->ls->info)
 273        &&
 274        greaterThan(s->rs->info)
 275        &&
 276        invSetAsBinTree(s->l)
 277        &&
 278        invSetAsBinTree(s->r));
 279}
 280
 281/*--------------------*/
 282
 283static int
 284invNoRedNodeHasRedChild(Set s)
 285{
 286  if (isEmptySet(s))
 287    return 1;
 288
 289  return
 290    (isBlackNode(s)
 291     ||
 292     ! hasRedChild(s)
 293    )
 294    &&
 295    invNoRedNodeHasRedChild(s->l)
 296    &&
 297    invNoRedNodeHasRedChild(s->r);
 298}
 299
 300/*--------------------*/
 301
 302static int
 303noOfBlackNodes(Set s)
 304{
 305  if (isEmptySet(s))
 306    return 1;
 307
 308  {
 309    int nl = noOfBlackNodes(s->l);
 310    int nr = noOfBlackNodes(s->r);
 311
 312    if (nl == nr && nl != -1)
 313      return nl + isBlackNode(s);
 314
 315    /* invariant does not hold */
 316    return -1;
 317  }
 318}
 319
 320/*--------------------*/
 321
 322int
 323invSetAsRedBlackTree(Set s)
 324{
 325  return
 326    invSetAsBinTree(s)
 327    &&
 328    invNoRedNodeHasRedChild(s)
 329    &&
 330    noOfBlackNodes(s) != -1;
 331}
 332
 333/*--------------------*/
 334
 335unsigned int
 336card(Set s)
 337{
 338  if (isEmptySet(s))
 339    return 0;
 340
 341  return card(s->l) + 1 + card(s->r);
 342}
 343
 344/*--------------------*/
 345
 346static unsigned int
 347max(unsigned int iunsigned int j)
 348{
 349  return i > j ? i : j;
 350}
 351
 352/*--------------------*/
 353
 354static unsigned int
 355min(unsigned int iunsigned int j)
 356{
 357  return i < j ? i : j;
 358}
 359
 360/*--------------------*/
 361
 362unsigned int
 363maxPathLength(Set s)
 364{
 365  if (isEmptySet(s))
 366    return 0;
 367
 368  return
 369    1 + max(maxPathLength(s->l),
 370            maxPathLength(s->r));
 371}
 372
 373/*--------------------*/
 374
 375unsigned int
 376minPathLength(Set s)
 377{
 378  if (isEmptySet(s))
 379    return 0;
 380
 381  return
 382    1 + min(minPathLength(s->l),
 383            minPathLength(s->r));
 384}
 385
 386/*--------------------*/
weiter

weiter

Ein einfacher Test: Test.c

   1#include <stdio.h>
   2#include <assert.h>
   3
   4#include "Element.h"
   5#include "Set.h"
   6
   7int
   8main(int argcchar *argv[])
   9{
  10  unsigned int noOfElems = 1000;
  11
  12  if (argc == 2)
  13    sscanf(argv[1]"%u"&noOfElems);
  14
  15  fprintf(stderr,
  16          "%s %u %s",
  17          "run red black trees with inserting",
  18          noOfElems,
  19          "elements in ascending order\n"
  20          );
  21
  22  {
  23    Set s = mkEmptySet();
  24    unsigned int i;
  25
  26    for (i = 0; i < noOfElems++i)
  27      {
  28        s = insertElem((Element) is);
  29      }
  30
  31    fprintf(stderr,
  32            "card(s)          = %u\n",
  33            card(s));
  34    fprintf(stderr,
  35            "minPathLength(s) = %u\n",
  36            minPathLength(s));
  37    fprintf(stderr,
  38            "maxPathLength(s) = %u\n",
  39            maxPathLength(s));
  40  }
  41
  42  return 0;
  43}
weiter

weiter

Übersetzen:

make clean Test

weiter

weiter

worst case Test: Sortiertes Einfügen mit 7 Elementen

make run CARD=7

weiter

weiter

worst case Test: Sortiertes Einfügen mit 100.000 Elementen

make run CARD=100000

weiter

weiter

worst case Test: Sortiertes Einfügen mit 1.000.000 Elementen

make run CARD=1000000

weiter

weiter

worst case Test: Sortiertes Einfügen mit 2.000.000 Elementen

make run CARD=2000000

weiter

weiter

worst case Test: Sortiertes Einfügen mit 4.000.000 Elementen

make run CARD=4000000

weiter

weiter

worst case Test: Sortiertes Einfügen mit 8.000.000 Elementen

make run CARD=8000000

weiter

weiter

Download

Quellen
Element.h
Element.c
Set.h
Set.c
Test.c
Makefile

Letzte Änderung: 04.01.2011
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel