En encadenamiento, el factor de carga alpha = cantidad / capacidad indica cuantas claves en promedio hay por bucket. Al crecer alpha aumentan las colisiones y las listas por bucket se alargan. Un rehash crea una tabla mas grande y reubica cada nodo, recuperando el tiempo promedio O(1).
alpha >= 1.0 (una colision promedio por bucket).alpha mayor, pero mide la longitud maxima de listas.NULL.#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct HashNode {
const char *clave;
int valor;
struct HashNode *sig;
} HashNode;
typedef struct {
HashNode **buckets;
unsigned long capacidad;
unsigned long cantidad;
} HashTable;
static unsigned long hash_cadena(const char *clave, unsigned long cap) {
unsigned long h = 5381;
int c;
while ((c = *clave++) != 0) {
h = ((h << 5) + h) + (unsigned long)c; /* djb2 */
}
return h % cap;
}
HashTable *hash_crear(unsigned long capacidad) {
HashTable *t = malloc(sizeof(HashTable));
if (!t) return NULL;
t->capacidad = capacidad;
t->cantidad = 0;
t->buckets = calloc(capacidad, sizeof(HashNode *));
return t;
}
int hash_insertar(HashTable *t, const char *clave, int valor) {
unsigned long idx = hash_cadena(clave, t->capacidad);
HashNode *nodo = t->buckets[idx];
while (nodo != NULL) {
if (strcmp(nodo->clave, clave) == 0) {
nodo->valor = valor;
return 1;
}
nodo = nodo->sig;
}
HashNode *nuevo = malloc(sizeof(HashNode));
if (!nuevo) return 0;
nuevo->clave = clave;
nuevo->valor = valor;
nuevo->sig = t->buckets[idx];
t->buckets[idx] = nuevo;
t->cantidad++;
return 1;
}
static int hash_reinsertar(HashTable *destino, const char *clave, int valor) {
unsigned long idx = hash_cadena(clave, destino->capacidad);
HashNode *nuevo = malloc(sizeof(HashNode));
if (!nuevo) return 0;
nuevo->clave = clave;
nuevo->valor = valor;
nuevo->sig = destino->buckets[idx];
destino->buckets[idx] = nuevo;
destino->cantidad++;
return 1;
}
int hash_rehash(HashTable *t, unsigned long nueva_capacidad) {
HashNode **nuevos_buckets = calloc(nueva_capacidad, sizeof(HashNode *));
if (!nuevos_buckets) return 0;
HashTable destino = { nuevos_buckets, nueva_capacidad, 0 };
for (unsigned long i = 0; i < t->capacidad; i++) {
HashNode *n = t->buckets[i];
while (n != NULL) {
HashNode *sig = n->sig;
if (!hash_reinsertar(&destino, n->clave, n->valor)) {
/* en caso de fallo, liberar lo nuevo */
for (unsigned long j = 0; j < destino.capacidad; j++) {
HashNode *p = destino.buckets[j];
while (p) {
HashNode *tmp = p->sig;
free(p);
p = tmp;
}
}
free(nuevos_buckets);
return 0;
}
free(n);
n = sig;
}
}
free(t->buckets);
t->buckets = nuevos_buckets;
t->capacidad = nueva_capacidad;
t->cantidad = destino.cantidad;
return 1;
}
Nota: se libera cada nodo antiguo tras reinsertarlo. Si las claves provienen de strings dinamicos, asegúrate de decidir si se copian o se comparten.
Ejemplo autocontenido que dispara rehash cuando alpha supera 1.0. Copia este archivo como main.c en un proyecto de consola de CLion para ver el crecimiento y la redistribucion de buckets.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct HashNode {
const char *clave;
int valor;
struct HashNode *sig;
} HashNode;
typedef struct {
HashNode **buckets;
unsigned long capacidad;
unsigned long cantidad;
} HashTable;
static unsigned long hash_cadena(const char *clave, unsigned long cap) {
unsigned long h = 5381;
int c;
while ((c = *clave++) != 0) {
h = ((h << 5) + h) + (unsigned long)c; /* djb2 */
}
return h % cap;
}
HashTable *hash_crear(unsigned long capacidad) {
HashTable *t = malloc(sizeof(HashTable));
if (!t) return NULL;
t->capacidad = capacidad;
t->cantidad = 0;
t->buckets = calloc(capacidad, sizeof(HashNode *));
return t;
}
int hash_insertar(HashTable *t, const char *clave, int valor) {
unsigned long idx = hash_cadena(clave, t->capacidad);
HashNode *nodo = t->buckets[idx];
while (nodo != NULL) {
if (strcmp(nodo->clave, clave) == 0) {
nodo->valor = valor;
return 1;
}
nodo = nodo->sig;
}
HashNode *nuevo = malloc(sizeof(HashNode));
if (!nuevo) return 0;
nuevo->clave = clave;
nuevo->valor = valor;
nuevo->sig = t->buckets[idx];
t->buckets[idx] = nuevo;
t->cantidad++;
return 1;
}
static int hash_reinsertar(HashTable *destino, const char *clave, int valor) {
unsigned long idx = hash_cadena(clave, destino->capacidad);
HashNode *nuevo = malloc(sizeof(HashNode));
if (!nuevo) return 0;
nuevo->clave = clave;
nuevo->valor = valor;
nuevo->sig = destino->buckets[idx];
destino->buckets[idx] = nuevo;
destino->cantidad++;
return 1;
}
int hash_rehash(HashTable *t, unsigned long nueva_capacidad) {
HashNode **nuevos_buckets = calloc(nueva_capacidad, sizeof(HashNode *));
if (!nuevos_buckets) return 0;
HashTable destino = { nuevos_buckets, nueva_capacidad, 0 };
for (unsigned long i = 0; i < t->capacidad; i++) {
HashNode *n = t->buckets[i];
while (n != NULL) {
HashNode *sig = n->sig;
if (!hash_reinsertar(&destino, n->clave, n->valor)) {
for (unsigned long j = 0; j < destino.capacidad; j++) {
HashNode *p = destino.buckets[j];
while (p) {
HashNode *tmp = p->sig;
free(p);
p = tmp;
}
}
free(nuevos_buckets);
return 0;
}
free(n);
n = sig;
}
}
free(t->buckets);
t->buckets = nuevos_buckets;
t->capacidad = nueva_capacidad;
t->cantidad = destino.cantidad;
return 1;
}
void hash_destruir(HashTable *t) {
for (unsigned long i = 0; i < t->capacidad; i++) {
HashNode *n = t->buckets[i];
while (n) {
HashNode *tmp = n->sig;
free(n);
n = tmp;
}
}
free(t->buckets);
free(t);
}
int main(void) {
HashTable *tabla = hash_crear(4);
const char *claves[] = { "juan", "ana", "luis", "pedro", "maria", "sofia" };
int valores[] = { 50, 23, 19, 90, 41, 77 };
size_t n = sizeof(claves) / sizeof(claves[0]);
for (size_t i = 0; i < n; i++) {
double alpha = (double)(tabla->cantidad + 1) / (double)tabla->capacidad;
if (alpha >= 1.0) {
unsigned long nueva = tabla->capacidad * 2 + 1; /* siguiente impar */
printf("Rehash a capacidad %lu (alpha previsto %.2f)\n", nueva, alpha);
hash_rehash(tabla, nueva);
}
hash_insertar(tabla, claves[i], valores[i]);
}
printf("Tabla final:\n");
for (unsigned long i = 0; i < tabla->capacidad; i++) {
printf("[%lu]: ", i);
HashNode *nodo = tabla->buckets[i];
while (nodo) {
printf("(%s -> %d) ", nodo->clave, nodo->valor);
nodo = nodo->sig;
}
printf("\n");
}
hash_rehash(tabla, tabla->capacidad * 2 + 1);
printf("Tras rehash manual:\n");
for (unsigned long i = 0; i < tabla->capacidad; i++) {
printf("[%lu]: ", i);
HashNode *nodo = tabla->buckets[i];
while (nodo) {
printf("(%s -> %d) ", nodo->clave, nodo->valor);
nodo = nodo->sig;
}
printf("\n");
}
hash_destruir(tabla);
return 0;
}
Ejecutalo en tu proyecto de consola y observa como la capacidad crece antes de que el factor de carga supere 1.0.
Inserta juan, ana, luis, pedro, maria y sofia en una tabla que rehash al superar alpha 1.0. Se duplican buckets (capacidad * 2 + 1) y se reinsertan todos los nodos.