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.