/*
* Copyright 2014 YU Heng-yang. All rights reserved.
*
* hashtable.c - Simple hash table implementation.
*
* Modify List to any other types as you need as long as
* the list structure is retained. Remember also to provide
* the hash function and key compare function if necessary.
*
* 2014-7-7 YU Heng-yang.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define ALLOC(size) malloc(size)
#define FREE(p) free(p)
typedef struct List List;
struct List {
const char *key; /* can be other types */
void *data; /* generic, can be a specific type if necessary */
List *next;
};
typedef struct HTable *HTable;
struct HTable {
unsigned size;
int (*hash) (const char *key);
int (*cmp) (const void *x, const void *y);
List **buckets;
};
HTable htable_new(unsigned hint, int (*hash) (const char *key),
int (*cmp) (const void *x, const void *y));
/* create new item if data is not found and create is nonzero */
void *htable_lookup(HTable table, const char *key, void *data, int create);
void *htable_remove(HTable table, const char *key);
HTable htable_destroy(HTable table);
void htable_traversal(HTable table, void (*apply) (void *data, void *arg),
void *arg);
void print(void *data, void *arg)
{
assert(arg);
printf((char *)arg, (char *)data);
}
int main(int argc, char *argv[])
{
#define N 5
int i;
char str[32], format[5];
char *keys[N] = {
"abc",
"sjdfkl",
"12ASJ34lj",
"89",
"weuriozuioeaZLBN"
};
/* use default hash and key compare functions, 30 elements */
HTable table = htable_new(30, NULL, NULL);
sprintf(format, "%%%ds", sizeof(str) - 1);
for (i = 0; i < N; i++) {
char *data;
fscanf(stdin, format, str);
str[sizeof(str) - 1] = '\0';
data = strcpy(ALLOC(strlen(str) + 1), str); /* bad! */
htable_lookup(table, keys[i], data, 1);
printf("After insert %s: ", data);
htable_traversal(table, print, "%s ");
putchar('\n');
}
for (i = 0; i < N; i++) {
void *data = htable_remove(table, keys[i]);
assert(data);
FREE(data);
}
table = htable_destroy(table);
return 0;
}
static int default_hash(const char *key);
static int default_cmp(const void *x, const void *y);
static unsigned next_prime(unsigned hint);
HTable htable_new(unsigned hint,
int (*hash) (const char *key),
int (*cmp) (const void *x, const void *y))
{
HTable table;
unsigned size = next_prime(hint);
assert(size);
table = ALLOC(sizeof(*table));
assert(table);
table->buckets = ALLOC(size * sizeof(**table->buckets));
assert(table->buckets);
table->size = size;
table->hash = hash ? hash : default_hash;
table->cmp = cmp ? cmp : default_cmp;
memset(table->buckets, 0, size * sizeof(**table->buckets));
return table;
}
/* create new item if data is not found and create is nonzero */
void *htable_lookup(HTable table, const char *key, void *data, int create)
{
assert(table);
assert(key);
unsigned index = table->hash(key) % table->size;
List *p;
for (p = table->buckets[index]; p; p = p->next)
if (0 == table->cmp(key, p->key))
break; /* found */
/* not found, create if needed */
if (!p && create) {
p = ALLOC(sizeof(*p));
assert(p);
p->key = key; /* assume key won't be modified */
p->data = data;
p->next = table->buckets[index];
table->buckets[index] = p;
}
return p ? p->data : NULL;
}
void *htable_remove(HTable table, const char *key)
{
assert(table && key);
unsigned index = table->hash(key) % table->size;
List **pp;
void *data = NULL;
for (pp = &table->buckets[index]; *pp; pp = &(*pp)->next)
if (0 == table->cmp((*pp)->key, key)) {
List *p = *pp;
*pp = p->next;
data = p->data;
FREE(p);
break;
}
return data;
}
HTable htable_destroy(HTable table)
{
assert(table);
/* assume data is freed some where else */
FREE(table->buckets);
memset(table, 0, sizeof(*table));
FREE(table);
return NULL;
}
void htable_traversal(HTable table, void (*apply) (void *data, void *arg),
void *arg)
{
assert(table);
assert(apply);
int i;
List *p;
for (i = 0; i < table->size; i++)
for (p = table->buckets[i]; p; p = p->next)
apply(p->data, arg);
}
/* see The Practice of Programming, p.57 */
static int default_hash(const char *key)
{
const int MULTIPLIER = 31;
unsigned h;
const unsigned char *p;
for (h = 0, p = (const unsigned char *)key; *p; p++)
h = MULTIPLIER * h + *p;
return h;
}
static int default_cmp(const void *x, const void *y)
{
return strcmp((const char *)x, (const char *)y);
}
static unsigned next_prime(unsigned size)
{
/*
* Just randomly chosen prime numbers between 1 and 10000, not
* empirical data. Add more primes as you need if table
* is expected to be very large.
*/
static unsigned primes[] = {
31, 53, 67, 79, 97, 113, 127, 139, 151, 163,
181, 211, 257, 307, 409, 577, 683, 827, 1013, 1571,
2833, 3347, 3571, 4989, 5833, 6469, 7518, 9999
};
int i;
for (i = 0; i < sizeof(primes) / sizeof(primes[0]); i++)
if (primes[i] > size)
return primes[i];
assert(0 == "table size is too large!");
return 0;
}
哈希表
最新推荐文章于 2024-06-30 13:08:33 发布