El ordenamiento topológico permite listar los vértices de un digrafo respetando la dirección de cada arista. Si existe una arista u → v, entonces u debe aparecer antes que v en el orden. Se usa para planificar tareas, resolver dependencias de software, compilar módulos, orquestar pipelines y más.
El orden topológico solo existe si el grafo es un DAG:
Si hay un ciclo, por ejemplo A → B → C → A, no se puede linealizar porque cada vértice depende de otro que vuelve. Cualquier orden topológico es una manera de aplanar el DAG respetando sus dependencias.
Parte de los vértices con grado de entrada 0 (sin dependencias) y los procesa en una cola.
in-degree = 0 y encolarlos.v y agregarlo al orden topológico.in-degree de cada vecino.in-degree = 0, encolarlo.Si se procesan todos los vértices, el grafo es un DAG; si quedan sin procesar, existe un ciclo.
Ejemplo (A → C, B → C, C → D):
Otra forma es usar DFS y apilar nodos al terminar sus vecinos.
v, insertarlo al frente de una lista (o pushear en un stack).La intuición: un nodo entra en la lista solo cuando todas sus dependencias ya se resolvieron.
El DFS también permite detectar ciclos con un arreglo de colores: blanco (no visitado), gris (en proceso), negro (finalizado). Una arista hacia un nodo gris indica ciclo.
in-degree > 0, hay un ciclo.Tarea_A → Tarea_B → Tarea_C → Tarea_A; no existe orden válido.A.h, B.h, C.h).Implementación del algoritmo de Kahn sobre lista de adyacencia (grafos dirigidos). Si hay ciclo, el orden no es posible.
#include <stdio.h>
#include <stdlib.h>
typedef struct Arista {
int destino;
struct Arista *sig;
} Arista;
typedef struct {
int n;
Arista **listas;
} Grafo;
Grafo crearGrafo(int n) {
Grafo g;
g.n = n;
g.listas = calloc(n, sizeof(Arista *));
return g;
}
void agregarAristaDirigida(Grafo *g, int u, int v) {
Arista *a = malloc(sizeof(Arista));
a->destino = v;
a->sig = g->listas[u];
g->listas[u] = a;
}
int ordenTopologicoKahn(const Grafo *g, int *orden) {
int *indeg = calloc(g->n, sizeof(int));
for (int u = 0; u < g->n; u++) {
for (Arista *it = g->listas[u]; it; it = it->sig) {
indeg[it->destino]++;
}
}
int *cola = malloc(g->n * sizeof(int));
int ini = 0, fin = 0;
for (int i = 0; i < g->n; i++) {
if (indeg[i] == 0) cola[fin++] = i;
}
int idx = 0;
while (ini < fin) {
int u = cola[ini++];
orden[idx++] = u;
for (Arista *it = g->listas[u]; it; it = it->sig) {
int v = it->destino;
if (--indeg[v] == 0) {
cola[fin++] = v;
}
}
}
free(indeg);
free(cola);
return idx; /* cantidad de nodos ordenados */
}
int main(void) {
Grafo g = crearGrafo(6);
agregarAristaDirigida(&g, 0, 2);
agregarAristaDirigida(&g, 0, 3);
agregarAristaDirigida(&g, 1, 3);
agregarAristaDirigida(&g, 1, 4);
agregarAristaDirigida(&g, 3, 5);
agregarAristaDirigida(&g, 2, 5);
int *orden = malloc(g.n * sizeof(int));
int colocados = ordenTopologicoKahn(&g, orden);
if (colocados != g.n) {
printf("Hay un ciclo dirigido, no existe orden topologico.\n");
} else {
printf("Orden topologico (Kahn):\n");
for (int i = 0; i < g.n; i++) {
printf("%d%s", orden[i], (i + 1 == g.n) ? "\n" : " ");
}
}
free(orden);
return 0;
}
Versión recursiva con detección de ciclos usando colores (0 no visitado, 1 en proceso, 2 finalizado). Si hay back-edge, no existe orden topológico.
#include <stdio.h>
#include <stdlib.h>
typedef struct Arista {
int destino;
struct Arista *sig;
} Arista;
typedef struct {
int n;
Arista **listas;
} Grafo;
Grafo crearGrafo(int n) {
Grafo g;
g.n = n;
g.listas = calloc(n, sizeof(Arista *));
return g;
}
void agregarAristaDirigida(Grafo *g, int u, int v) {
Arista *a = malloc(sizeof(Arista));
a->destino = v;
a->sig = g->listas[u];
g->listas[u] = a;
}
int dfsTopo(const Grafo *g, int u, int *color, int *stack, int *top) {
color[u] = 1; /* en proceso */
for (Arista *it = g->listas[u]; it; it = it->sig) {
int v = it->destino;
if (color[v] == 0) {
if (!dfsTopo(g, v, color, stack, top)) return 0;
} else if (color[v] == 1) {
return 0; /* ciclo */
}
}
color[u] = 2; /* finalizado */
stack[(*top)++] = u;
return 1;
}
int ordenTopologicoDFS(const Grafo *g, int *orden) {
int *color = calloc(g->n, sizeof(int));
int *stack = malloc(g->n * sizeof(int));
int top = 0;
for (int u = 0; u < g->n; u++) {
if (color[u] == 0) {
if (!dfsTopo(g, u, color, stack, &top)) {
free(color);
free(stack);
return 0; /* hay ciclo */
}
}
}
for (int i = 0; i < top; i++) {
orden[i] = stack[top - 1 - i]; /* invertir stack */
}
free(color);
free(stack);
return 1;
}
int main(void) {
Grafo g = crearGrafo(6);
agregarAristaDirigida(&g, 0, 2);
agregarAristaDirigida(&g, 0, 3);
agregarAristaDirigida(&g, 1, 3);
agregarAristaDirigida(&g, 1, 4);
agregarAristaDirigida(&g, 3, 5);
agregarAristaDirigida(&g, 2, 5);
int *orden = malloc(g.n * sizeof(int));
if (!ordenTopologicoDFS(&g, orden)) {
printf("Hay un ciclo dirigido, no existe orden topologico.\n");
} else {
printf("Orden topologico (DFS):\n");
for (int i = 0; i < g.n; i++) {
printf("%d%s", orden[i], (i + 1 == g.n) ? "\n" : " ");
}
}
free(orden);
return 0;
}