Una tabla hash usa un array como almacenamiento principal y una funcion hash para traducir la clave en un indice. Cada posicion del array es un bucket que puede guardar un elemento o una estructura auxiliar si hay colisiones.
#include <stdlib.h>
typedef struct {
const char *clave;
int valor;
int ocupado; /* 0 = libre, 1 = ocupado */
} Entrada;
typedef struct {
Entrada *buckets;
unsigned long capacidad;
unsigned long cantidad;
} TablaHash;
TablaHash *tabla_crear(unsigned long capacidad) {
TablaHash *t = malloc(sizeof(TablaHash));
t->capacidad = capacidad;
t->cantidad = 0;
t->buckets = calloc(capacidad, sizeof(Entrada));
return t;
}
El array buckets guarda entradas alineadas en memoria contigua, lo que facilita indexar y recorrer cuando se rehash.
La clave se transforma con la funcion hash en un entero, y el modulo de la capacidad define el bucket:
unsigned long indice = hash_cadena(clave) % tabla->capacidad;
Ese indice apunta al bucket donde se intenta insertar o buscar. Si el bucket esta ocupado por otra clave, se aplica la estrategia de colisiones (que detallaremos en temas posteriores).
El tamano inicial (capacidad) suele elegirse como potencia de dos o numero primo para mejorar la dispersion. Tablas muy pequeñas generan colisiones inmediatas; tablas enormes desperdician memoria. Lo habitual es comenzar con 8, 16 o 32 buckets y crecer segun el uso.
El factor de carga alpha es la relacion entre elementos guardados y capacidad total:
double alpha = (double)tabla->cantidad / (double)tabla->capacidad;
Valores comunes de referencia:
Rehashing significa crear un nuevo array mas grande y reinsertar cada elemento calculando de nuevo su indice. Flujo basico:
Este proceso mantiene la complejidad amortizada O(1), aunque cada rehash puntual cuesta O(n) porque se mueven todos los elementos.