Algoritmer och Datastrukturer
Kary FRÄMLING
Kap. 10, Sid 1
C-språket 2/Kary Främling v2000
HASHTABELLER
En hashtabell är den effektivaste datastrukturen för att lagra data så att den kan både sparas och
hittas så snabbt som möjligt. T.ex. för att hitta en persons uppgifter på basen av dess
socialskyddssignum är en hashtabell mycket väl lämpad.
En hashtabell har en viss, given storlek som vanligtvis är ett primtal. Orsaken till att storleken helst
skall vara ett primtal är att det underlättar att fördela datan jämnt inne i tabellen.
Då ett nytt dataobjekt skall läggas in i hashtabellen, räknas ett tabellindex på basen av den data som
skall lagras. Denna hashfunktion skall helst returnera index som är så väl utspridda som möjligt
över hela tabellen.
För teckensträngar kan detta index räknas genom att ta divisionresten (mod, %) mellan summan av
alla tecken i strängen och hashtabellens storlek. Detta är fallet i Exempel 1.
Eftersom det inte är möjligt att garantera att hashtabellens element med det givna indexet inte redan
är utnyttjat, är det nödvändigt att ha en ”reservplan”. Denna går ut på att använda länkade listor,
vars lagringskapacitet inte är begränsad.
För att sökningen i hashtabellen skall vara effektiv får de länkade listorna inte bli för långa. Ifall så
sker borde en större hashtabell skapas, till vilken all data kopieras. Detta är inte önskvärt, varför
hashtabellens storlek helst borde väljas så bra som möjligt från början.
Exempel 1. Modulärt skriven hashtabell för lagring av vilken typs data som helst (kompilerat
och kört).
/* Hash.h */
typedef struct hash_node {
void *data;
struct hash_node
*next;
} HashNode;
typedef struct hash_table {
HashNode **hTable;
int
hSize;
} HashTable;
HashTable *newHashTable(int hsize);
int add(HashTable *ht, void *obj, int objSize);
void show(HashTable *ht);
void freeHashTable(HashTable *ht);
Algoritmer och Datastrukturer
Kary FRÄMLING
/* HashMain.c */
#include <stdio.h>
#include <string.h>
#include "Hash.h"
#define
#define
H_SIZE
19
MAX_LINE_LEN 1000
main()
{
HashTable *ht;
int len;
char line[MAX_LINE_LEN];
/* Skapa ny hashtabell */
ht = newHashTable(H_SIZE);
/* Fyll pa den */
printf("Mata in teckenstrangar, avsluta med en tom rad:\n");
while ( (len = strlen(gets(line))) > 0 ) {
add(ht, line, len + 1);
}
/* Visa innehallet */
show(ht);
/* Frigor alltihopa */
freeHashTable(ht);
}
/* Hash.c */
#include <stdio.h>
#include <stdlib.h>
#include <mem.h>
#include "hash.h"
static int hashFunction(int hSize, void *obj, int objSize);
HashTable *newHashTable(int hSize)
{
HashTable *ht;
ht = (HashTable*) calloc(1, sizeof(HashTable));
ht->hTable = (HashNode**) calloc(hSize, sizeof(HashNode*));
ht->hSize = hSize;
return ht;
}
int add(HashTable *ht, void *obj, int objSize)
{
int hval;
HashNode *hn;
/* Skapa en ny nod och kopiera in datan i den */
hn = calloc(1, sizeof(HashNode));
hn->data = malloc(objSize);
memcpy(hn->data, obj, objSize);
Kap. 10, Sid 2
Algoritmer och Datastrukturer
Kary FRÄMLING
Kap. 10, Sid 3
/* Placera in noden i hashtabellen */
hval = hashFunction(ht->hSize, obj, objSize);
printf("%d\n", hval);
/* Skriv ut bara for att kunna verifiera */
if ( ht->hTable[hval] == NULL ) {
ht->hTable[hval] = hn;
}
else {
hn->next = ht->hTable[hval];
ht->hTable[hval] = hn;
}
return 1; /* Successful completion */
}
void show(HashTable *ht)
{
int i;
HashNode *node;
for ( i = 0 ; i < ht->hSize ; i++ ) {
for ( node = ht->hTable[i] ; node != NULL ; node = node->next ) {
printf("%s\n", (char*) node->data);
}
}
}
void freeHashTable(HashTable *ht)
{
int i;
HashNode *node, *tmp;
for ( i = 0 ; i < ht->hSize ; i++ ) {
for ( node = ht->hTable[i] ; node != NULL ; node = tmp ) {
tmp = node->next;
free(node->data);
free(node);
}
}
free(ht->hTable);
free(ht);
}
static int hashFunction(int hSize, void *obj, int objSize)
{
int i, hval = 0;
char *p;
for ( i = 0, p = (char*) obj ; i < objSize ; i++ )
hval += *p++;
return hval%hSize;
}
Programmet körs:
Algoritmer och Datastrukturer
Kary FRÄMLING
Kap. 10, Sid 4
Mata in teckenstrangar, avsluta med en tom rad:
olle
10
pelle
17
ville
8
johannes
18
kalle
8
kalle
ville
olle
pelle
johannes
Programkörningen visar det index som hashfunktionen gett för varje teckensträng. ”kalle” och
”ville” har båda fått samma index så ”kalle” och ”ville” är båda i samma länkade lista.
Exemplet visar en ytterst allmän modul för hashtabeller eftersom den kan lagra data av vilken typ
som helst. Detta är möjligt genom att datan som skall sparas tas emot genom pekare till ”void” och
att den datatypsoberoende funktionen ”memcpy()” använts. Funktionen ”show()” är den enda som
är datatypsberoende eftersom den antar att den lagrade datan är en teckensträng.
Den visade implementeringen av en hashtabell är troligtvis den vanligaste. I litteraturen hittas dock
en hel del andra hashfunktioner samt lösningar som inte använder länkade listor.