Los constructores preparan la estructura interna del grafo antes de ejecutar algoritmos. A continuación se muestran variantes comunes en C usando matrices y listas, junto con la liberación segura de memoria.
Iniciar la estructura sin aristas evita valores basura y deja el grafo listo para insertar conexiones.
typedef struct {
int n;
int **m;
} GrafoMatriz;
GrafoMatriz *crearGrafoVacio(int n) {
GrafoMatriz *g = malloc(sizeof(GrafoMatriz));
if (!g) return NULL;
g->n = n;
g->m = malloc(n * sizeof(int *));
if (!g->m) { free(g); return NULL; }
for (int i = 0; i < n; i++) {
g->m[i] = calloc(n, sizeof(int));
if (!g->m[i]) {
for (int k = 0; k < i; k++) free(g->m[k]);
free(g->m);
free(g);
return NULL;
}
}
return g;
}
Si ya tenemos una matriz de adyacencia (por ejemplo, leída de archivo), podemos copiarla al grafo.
GrafoMatriz *crearDesdeMatriz(int n, int origen[][4]) {
GrafoMatriz *g = crearGrafoVacio(n);
if (!g) return NULL;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
g->m[i][j] = origen[i][j];
}
}
return g;
}
Cuando la entrada es una lista de aristas (u, v, peso), podemos construir tanto matrices como listas de adyacencia. Aquí se muestra el camino hacia una lista.
typedef struct Arista {
int u, v, peso;
} AristaInput;
typedef struct Nodo {
int destino;
int peso;
struct Nodo *sig;
} Nodo;
typedef struct {
int n;
Nodo **listas;
} GrafoLista;
GrafoLista *crearDesdeAristas(int n, const AristaInput *aristas, int cant, int esDirigido) {
GrafoLista *g = malloc(sizeof(GrafoLista));
if (!g) return NULL;
g->n = n;
g->listas = calloc(n, sizeof(Nodo *));
if (!g->listas) { free(g); return NULL; }
for (int i = 0; i < cant; i++) {
int u = aristas[i].u;
int v = aristas[i].v;
int peso = aristas[i].peso;
Nodo *nuevo = malloc(sizeof(Nodo));
if (!nuevo) continue;
nuevo->destino = v;
nuevo->peso = peso;
nuevo->sig = g->listas[u];
g->listas[u] = nuevo;
if (!esDirigido) {
Nodo *rev = malloc(sizeof(Nodo));
if (!rev) continue;
rev->destino = u;
rev->peso = peso;
rev->sig = g->listas[v];
g->listas[v] = rev;
}
}
return g;
}
Para permitir que la cantidad de vértices cambie en tiempo de ejecución, podemos realocar las estructuras. Este ejemplo ilustra una ampliación sencilla sobre lista de adyacencia.
int redimensionarGrafo(GrafoLista *g, int nuevoN) {
if (!g || nuevoN <= g->n) return 0; /* no reducimos en este ejemplo */
Nodo **nuevas = realloc(g->listas, nuevoN * sizeof(Nodo *));
if (!nuevas) return 0;
for (int i = g->n; i < nuevoN; i++) nuevas[i] = NULL;
g->listas = nuevas;
g->n = nuevoN;
return 1;
}
Es vital liberar todas las estructuras para evitar fugas.
void liberarGrafoMatriz(GrafoMatriz *g) {
if (!g) return;
for (int i = 0; i < g->n; i++) free(g->m[i]);
free(g->m);
free(g);
}
void liberarGrafoLista(GrafoLista *g) {
if (!g) return;
for (int u = 0; u < g->n; u++) {
Nodo *it = g->listas[u];
while (it) {
Nodo *tmp = it;
it = it->sig;
free(tmp);
}
}
free(g->listas);
free(g);
}
Usa el constructor que mejor se adapte a tu fuente de datos: matrices para cargas compactas, listas de aristas para flujos incrementales y redimensionamiento cuando el grafo crece durante la ejecución.