10. Representación de grafos mediante listas de adyacencia

Una lista de adyacencia guarda, para cada vértice, los vecinos conectados con él. Es una de las representaciones más utilizadas por su eficiencia y facilidad para recorrer grafos.

10.1 Introducción

Un dibujo ayuda a comprender un grafo, pero un programa necesita almacenarlo en memoria. Una de las formas más comunes consiste en asociar cada vértice con una colección de sus vecinos.

Esta estructura recibe el nombre de lista de adyacencia. Resulta especialmente conveniente cuando el grafo posee relativamente pocas aristas frente a la cantidad máxima posible.

10.2 Idea fundamental

Para cada vértice creamos una entrada que contiene todos los vértices conectados directamente con él.

A: [B, C]
B: [A, D]
C: [A]
D: [B]

La lista indica que A es adyacente a B y C; B es adyacente a A y D, y así sucesivamente.

10.3 Lista en grafos no dirigidos

En un grafo no dirigido, cada arista aparece en las listas de sus dos extremos. Si existe {A, B}, B se agrega a la lista de A y A se agrega a la lista de B.

arista {A, B}
A: [B]
B: [A]

Esta duplicación no representa dos aristas: son las dos incidencias de una única conexión no dirigida.

10.4 Lista en grafos dirigidos

En un grafo dirigido, la lista suele guardar los destinos alcanzables desde cada vértice. El arco A→B agrega B a la lista de A, pero no agrega A a la lista de B.

TipoConexiónResultado
No dirigidoA—BA: [B] y B: [A]
DirigidoA→BA: [B] y B: []

Si necesitamos consultar entradas con frecuencia, podemos mantener además una lista inversa con los arcos que llegan a cada vértice.

10.5 Simulación interactiva: del dibujo a la lista

Selecciona dos vértices para agregar o quitar una conexión. La lista de adyacencia se actualiza inmediatamente. En modo dirigido, el primer nodo es el origen y el segundo, el destino.

Lista de adyacencia

A: [B, C]
B: [A, D]
C: [A, E]
D: [B, E]
E: [C, D]
Selecciona dos vértices.
TipoNo dirigido
Vértices5
Aristas5

Observa cómo una arista no dirigida actualiza dos listas, mientras que un arco dirigido modifica solamente la lista de su origen.

10.6 Implementación con Map

En JavaScript, Map permite asociar cada vértice con un arreglo o conjunto de vecinos.

const grafo = new Map();

grafo.set("A", ["B", "C"]);
grafo.set("B", ["A", "D"]);
grafo.set("C", ["A"]);
grafo.set("D", ["B"]);

console.log(grafo.get("A"));
console.log(grafo.get("D"));

10.7 Agregar vértices y aristas

Una implementación reutilizable debe crear listas vacías para los vértices y actualizar ambos extremos al agregar una arista no dirigida.

const adyacencia = new Map();

function agregarVertice(v) {
  if (!adyacencia.has(v)) adyacencia.set(v, new Set());
}

function agregarArista(a, b) {
  agregarVertice(a);
  agregarVertice(b);
  adyacencia.get(a).add(b);
  adyacencia.get(b).add(a);
}

agregarArista("A", "B");
agregarArista("A", "C");
console.log([...adyacencia.get("A")]);

Utilizar Set evita insertar accidentalmente el mismo vecino más de una vez.

10.8 Consultar vecinos y grado

La lista ofrece acceso directo a todos los vecinos. En un grafo no dirigido, su longitud coincide con el grado del vértice si no hay bucles ni aristas múltiples.

vecinos(A) = lista[A]
grado(A) = longitud de lista[A]

Esta operación es eficiente porque no necesitamos revisar todas las aristas del grafo.

10.9 Complejidad espacial

Una lista de adyacencia almacena una entrada por vértice y referencias para las aristas.

Espacio: O(|V| + |E|)

En grafos no dirigidos cada arista aparece dos veces, pero el factor constante no modifica la complejidad. Esta eficiencia vuelve a las listas adecuadas para grafos dispersos.

10.10 Ventajas y desventajas

AspectoVentajaDesventaja
MemoriaUsa O(V + E)En grafos muy densos pierde ventaja
Recorrer vecinosAcceso directo y eficienteDepende del grado del vértice
Comprobar una aristaRápido con SetCon arreglos puede requerir búsqueda
Agregar conexionesOperación sencillaHay que mantener ambos extremos

10.11 Listas para grafos ponderados

Si las aristas tienen pesos, cada vecino puede almacenarse junto con el valor correspondiente.

A: [{vértice: B, peso: 4}, {vértice: C, peso: 7}]
B: [{vértice: A, peso: 4}]

Esta representación permite recorrer las conexiones y consultar sus costos, como requieren algoritmos de caminos mínimos.

10.12 Errores comunes

  • Olvidar crear la lista vacía de un vértice aislado.
  • Actualizar un solo extremo de una arista no dirigida.
  • Agregar el arco inverso automáticamente en un grafo dirigido.
  • Permitir vecinos duplicados sin que el modelo sea un multigrafo.
  • Confundir la cantidad de listas con la cantidad de aristas.
  • Eliminar un vértice sin quitarlo de las listas de sus vecinos.

10.13 Qué debes recordar de este tema

  • Una lista de adyacencia asocia cada vértice con sus vecinos.
  • En grafos no dirigidos, cada arista aparece en dos listas.
  • En grafos dirigidos, la lista suele guardar los destinos de salida.
  • Su complejidad espacial es O(V + E).
  • Permite recorrer eficientemente los vecinos de un vértice.
  • Puede almacenar pesos junto con cada vecino.

10.14 Conclusión

Las listas de adyacencia ofrecen una representación compacta y práctica para la mayoría de los grafos utilizados en programación. Su estructura coincide naturalmente con la operación de visitar vecinos.

En el próximo tema estudiaremos las matrices de adyacencia, que representan todas las parejas posibles mediante una tabla bidimensional.