Systemnahe Programmierung in Chome Systemnahe Programmierung in C: Hashtabellen Prof. Dr. Uwe Schmidt FH Wedel

Hashtabellen

weiter

weiter

Implementierung von Hashtabellen

Hashtabellen
werden zu effizienten Implementierung von Mengen und Tabellen verwendet. Voraussetzung ist eine Hashfuntion auf dem Element- oder Schlüssel-Datentyp.
 
Eine Hashfunktion ist eine Abbildung des Elementbereiches auf ein Intervall der natürlichen Zahlen [0 .. max-1].
Idee
Die Suche nach einem Element wird durch einen indizierten Zugriff in ein Feld realisiert. Wenn dieses alleine zum Ziel führen würde, hätte man eine Suche, die in O(1) laufen würde, also nicht mehr von der Größe der zu durchsuchenden Menge abhängen würde.
schlecht
Dieses Ziel kann nicht vollständig erreicht werden. Dazu wäre eine injektive Hashfunktion Voraussetzung. Solche Funktionen existieren im Allgemeinen nicht, da der Elementbereich üblicherweise unbeschränkt viele Elemente enthält.
gut
Aber es gibt sehr effizente Implementierungen, die für einen großen Bereich von Anwendungen diese Laufzeit fast erreichen.
Effizienz
Die Qualität der Implementierung hängt stark von der Wahl der Hashfunktion ab. Die Hashfunktion sollte alle Werte des Elementbereichs gleichmäßig auf das Intervall abbilden. Ähnliche Elemente sollten möglichst nicht auf den gleichen Wert abgebildet werden.
weiter
ist eine sehr rudimentäre Implementierung.
 
Sie verwendet ein Feld fester Länge. Elemente, deren Hashwert gleich dem eines schon eingetragenen Wertes ist, werden in die erste freie Stelle hinter dem Hashindex eingetragen.
 
Dieses funktioniert nur dann, wenn die Anzahl der zu speichernden Elemente kleiner als die Länge des Intervalls ist.
 
Sie funktioniert effizient nur dann, wenn die Anzahl signifikant kleiner ist, z.B. max 70% der Größe der Tabelle beträgt.
 
Das Löschen von Elementen ist nicht effizient zu realisieren.
weiter
ist eine allgemein einsetzbare Realisierung.
 
Sie besitzt nicht die oben beschriebenen Nachteile. Es wird mit dynamischer Anpassung der Tabellenlänge gearbeitet, um ein Gleichgewicht zwischen Speicherplatzausnutzung und Laufzeit zu erreichen.
 
Die einzelnen Tabelleneinträge sind verkettete Listen. Bei Verwendung einer guten Hashfunktion bleiben diese Listen aber sehr kurz (0 <= length <= 2).
weiter

weiter

Eine Schnittstelle für die Elemente: Element.h

   1#ifndef ELEMENT_H__
   2#define ELEMENT_H__ 1
   3
   4typedef char *Element;
   5
   6extern int compare(Element e1,
   7                   Element e2);
   8
   9extern unsigned int hash(Element e);
  10
  11#endif
weiter

weiter

Eine Implementierung für die Elemente: Element.c

   1#include "Element.h"
   2
   3#include <string.h>
   4
   5int
   6compare(Element e1Element e2)
   7{
   8  int cmp = strcmp(e1e2);
   9
  10  return (cmp >= 0) - (>= cmp);
  11}
  12
  13/* hash function as used in Java for Strings */
  14
  15unsigned int
  16hash(Element e)
  17{
  18  unsigned int res = 0;
  19
  20  while (*e != 0)
  21    {
  22      res = 31 * res + *e;
  23      ++e;
  24    }
  25  return res;
  26}
weiter

weiter

Die Schnittstelle für die beschränkt einsetzbare Hashtabelle: Hashtable0.h

   1#ifndef HASHTABLE0_H__
   2#define HASHTABLE0_H__
   3
   4/*--------------------*/
   5
   6#include "Element.h"
   7
   8typedef struct Entry *Set;
   9
  10struct Entry
  11{
  12  int used;
  13  Element info;
  14};
  15
  16/*--------------------*/
  17
  18extern Set mkEmptySet(void);
  19
  20extern int isInSet(Element eSet s);
  21
  22extern Set insertElem(Element eSet s);
  23
  24/* not implemented:
  25   extern Set removeElem(Element e, Set s);
  26*/
  27
  28extern int invSetAsHashtable(Set s);
  29
  30/*--------------------*/
  31
  32#endif
weiter

weiter

Die Implementierung für die beschränkt einsetzbare Hashtabelle: Hashtable0.c

   1#include "Hashtable0.h"
   2
   3#include <stdlib.h>
   4#include <stdio.h>
   5
   6#define eqElement(e1,e2) (compare((e1),(e2)) == 0)
   7
   8/* constant hashtable size */
   9/* not very flexible */
  10
  11#define size 1024
  12
  13/* counting modulo size */
  14/* call these macros only with simple variables */
  15
  16#define incr(i) i = (i == size - 1) ? 0 : i + 1
  17#define decr(i) i = ((i == 0) ? size : i) - 1
  18
  19/*--------------------*/
  20
  21
  22Set
  23mkEmpty(void)
  24{
  25  Set res = malloc(size * sizeof(*res));
  26
  27  if (!res)
  28    {
  29      perror
  30        ("mkEmptySet: malloc failed");
  31      exit(1);
  32    }
  33  {
  34    unsigned int i;
  35
  36    for (i = 0; i < size++i)
  37      res[i].used = 0;
  38  }
  39  return res;
  40}
  41
  42/*--------------------*/
  43
  44int
  45isInSet(Element eSet s)
  46{
  47  unsigned int i = hash(e) % size;
  48
  49  while (s[i].used
  50         && !eqElement(s[i].infoe))
  51    {
  52      incr(i);
  53    }
  54
  55  return s[i].used;
  56}
  57
  58/*--------------------*/
  59
  60Set
  61insertElem(Element eSet s)
  62{
  63  unsigned int i = hash(e) % size;
  64
  65  while (s[i].used
  66         && !eqElement(s[i].infoe))
  67    {
  68      incr(i);
  69    }
  70
  71  if (!s[i].used)
  72    {
  73      s[i].used = 1;
  74      s[i].info = e;
  75    }
  76
  77  return s;
  78}
  79
  80/*--------------------*/
  81
  82int
  83invSetAsHashtable(Set s)
  84{
  85  unsigned int i;
  86
  87  for (i = 0; i < size++i)
  88    {
  89      if (s[i].used)
  90        {
  91          unsigned int ix =
  92            hash(s[i].info) % size;
  93
  94          /* all entries in hashtable
  95             in the intervall [ix..i]
  96             must be filled, otherwise the hash does not
  97             fit to the position
  98           */
  99
 100          unsigned int j = i;
 101
 102          while (j != ix)
 103            {
 104              decr(j);
 105
 106              if (!s[j].used)
 107                return 0;
 108            }
 109        }
 110    }
 111
 112  return 1;
 113}
weiter

weiter

Die Schnittstelle für die allgemein einsetzbare Hashtabelle: Hashtable.h

   1#ifndef HASHTABLE_H__
   2#define HASHTABLE_H__
   3
   4/*--------------------*/
   5
   6#include "Element.h"
   7
   8typedef struct Node *List;
   9
  10struct Node
  11{
  12  Element info;
  13  List next;
  14};
  15
  16
  17typedef struct hashtable *Set;
  18
  19struct hashtable
  20{
  21  unsigned int size;
  22  unsigned int card;
  23  List *table;
  24};
  25
  26/*--------------------*/
  27
  28extern Set mkEmptySet(void);
  29extern int isEmptySet(Set s);
  30
  31extern int isInSet(Element eSet s);
  32
  33extern Set insertElem(Element eSet s);
  34extern Set removeElem(Element eSet s);
  35
  36extern unsigned int card(Set s);
  37
  38extern int invSetAsHashtable(Set s);
  39
  40/*--------------------*/
  41
  42#endif
weiter

weiter

Die Implementierung für die allgemein einsetzbare Hashtabelle: Hashtable.c

   1#include "Hashtable.h"
   2
   3#include <stdlib.h>
   4#include <stdio.h>
   5
   6/*--------------------*/
   7
   8/* auxiliary list functions */
   9
  10#define eqElement(e1,e2) (compare((e1),(e2)) == 0)
  11
  12#define mkEmptyList() ((List)0)
  13#define isEmptyList(l) ((l) == (List)0)
  14
  15static int
  16isInList(Element eList l)
  17{
  18  while (!isEmptyList(l))
  19    {
  20      if (eqElement(el->info))
  21        return 1;
  22      l = l->next;
  23    }
  24  return 0;
  25}
  26
  27/*--------------------*/
  28
  29static List
  30cons(Element eList l)
  31{
  32  /* the only call of malloc for List values */
  33  List res = malloc(sizeof(*l));
  34
  35  if (!res)
  36    {
  37      perror("cons: malloc failed");
  38      exit(1);
  39    }
  40  res->info = e;
  41  res->next = l;
  42
  43  return res;
  44}
  45
  46/*--------------------*/
  47
  48static List
  49removeElem1(Element eList l)
  50{
  51  if (isEmptyList(l))
  52    return l;
  53
  54  if (eqElement(el->info))
  55    {
  56      List l1 = l->next;
  57
  58      free(l);
  59      return l1;
  60    }
  61
  62  l->next = removeElem1(el->next);
  63  return l;
  64}
  65
  66
  67/*--------------------*/
  68
  69/* hash table creation */
  70
  71/* size for new empty hashtable */
  72#define initialSize 16
  73
  74Set
  75mkEmpty(void)
  76{
  77  Set res = malloc(sizeof(*res));
  78  List *table =
  79    malloc(initialSize *
  80           sizeof(*table));
  81
  82  if (!res || !table)
  83    {
  84      perror
  85        ("mkEmptySet: malloc failed");
  86      exit(1);
  87    }
  88
  89  res->size = initialSize;
  90  res->card = 0;
  91  res->table = table;
  92
  93  {
  94    unsigned int i;
  95
  96    for (i = 0; i < res->size++i)
  97      table[i] = mkEmptyList();
  98  }
  99
 100  return res;
 101}
 102
 103/*--------------------*/
 104
 105Set
 106resizeHashtable(Set s,
 107                unsigned int newsize)
 108{
 109  unsigned int size = s->size;
 110
 111  List *newtable =
 112    malloc(newsize * sizeof(*newtable));
 113
 114  List *oldtable = s->table;
 115
 116  unsigned int i;
 117
 118  /* initialize new table */
 119  for (i = 0; i < newsize++i)
 120    newtable[i] = mkEmptyList();
 121
 122  /* move entries from old table into new table */
 123  for (i = 0; i < size++i)
 124    {
 125      List l = oldtable[i];
 126
 127      while (!isEmptyList(l))
 128        {
 129          List l1 = l;
 130          unsigned int i2 =
 131            hash(l->info) % newsize;
 132
 133          l = l->next;
 134          l1->next = newtable[i2];
 135          newtable[i2] = l1;
 136        }
 137    }
 138
 139  free(oldtable);
 140
 141  s->size = newsize;
 142  s->table = newtable;
 143
 144  return s;
 145}
 146
 147/*--------------------*/
 148
 149/* if card > 1.0 * size, the table size is doubled */
 150/* this factor and the expansion factor can be tuned */
 151
 152Set
 153checkTableOverflow(Set s)
 154{
 155  /* don't use floating point arithmetic for this check */
 156  if (s->card > s->size)
 157    return resizeHashtable(s,
 158                           2 * s->size);
 159
 160  return s;
 161}
 162
 163/*--------------------*/
 164
 165/* if card < 0.25 * size, the table size is divided by 2 */
 166/* this factor and the shrinking factor can be tuned */
 167
 168Set
 169checkTableUnderflow(Set s)
 170{
 171  if (s->size > initialSize
 172      && 4 * s->card < s->size)
 173    return
 174      resizeHashtable(ss->size / 2);
 175
 176  return s;
 177}
 178
 179/*--------------------*/
 180
 181int
 182isInSet(Element eSet s)
 183{
 184  unsigned int i = hash(e) % s->size;
 185
 186  return isInList(es->table[i]);
 187}
 188
 189/*--------------------*/
 190
 191Set
 192insertElem(Element eSet s)
 193{
 194  unsigned int i = hash(e) % s->size;
 195  List l = s->table[i];
 196
 197  if (!isInList(el))
 198    {
 199      s->table[i] = cons(el);
 200      ++(s->card);
 201      s = checkTableOverflow(s);
 202    }
 203
 204  return s;
 205}
 206
 207Set
 208removeElem(Element eSet s)
 209{
 210  unsigned int i = hash(e) % s->size;
 211  List l = s->table[i];
 212
 213  if (isInList(el))
 214    {
 215      s->table[i] = removeElem1(el);
 216      --(s->card);
 217      s = checkTableUnderflow(s);
 218    }
 219
 220  return s;
 221}
 222
 223/*--------------------*/
 224
 225static int
 226checkHashes(Set s)
 227{
 228  unsigned int i;
 229
 230  for (i = 0; i < s->size++i)
 231    {
 232      List l = s->table[i];
 233
 234      while (!isEmptyList(l))
 235        {
 236          if (hash(l->info) % s->size !=
 237              i)
 238            return 0;
 239          l = l->next;
 240        }
 241    }
 242  return 1;
 243}
 244
 245static int
 246checkCard(Set s)
 247{
 248  unsigned int i;
 249  unsigned int res = 0;
 250
 251  for (i = 0; i < s->size++i)
 252    {
 253      List l = s->table[i];
 254
 255      while (!isEmptyList(l))
 256        {
 257          ++res;
 258          l = l->next;
 259        }
 260    }
 261  return res == s->card;
 262}
 263
 264static int
 265checkTableLevel(s)
 266{
 267  /* check whether size and
 268     card fit the space constraints */
 269  return 1;
 270}
 271
 272int
 273invSetAsHashtable(Set s)
 274{
 275  return
 276    checkHashes(s)
 277    &&
 278    checkCard(s)
 279    &&
 280    checkTableLevel(s);
 281}
weiter

weiter

Download

Quellen

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