Un Grafo es un conjunto de objetos llamados vértices (o nodos) unidos por enlaces llamados aristas. A diferencia de los árboles, en un grafo no hay jerarquía estricta (padre-hijo) y se permiten ciclos (caminos cerrados). Pueden ser Dirigidos (flechas) o No Dirigidos (líneas).

Imagen 50. Elementos de un Grafo (Vértices y Aristas)
Elementos fundamentales: Visualización de Vértices (V) conectados por Aristas (E). Diferencia visual entre un grafo dirigido (con flechas) y uno no dirigido.
No existe un tipo de dato "Grafo" nativo. Tenemos dos formas principales de representarlo: 1) Matriz de Adyacencia (tabla 2D de ceros y unos, rápida pero gasta mucha memoria) o 2) Lista de Adyacencia (array de listas enlazadas, eficiente en memoria para grafos dispersos).

Imagen 51. Matriz de Adyacencia vs Lista de Adyacencia
Esquema técnico: El mismo grafo representado a la izquierda como una cuadrícula (Matriz) y a la derecha como cadenas de nodos (Listas).
Para explorar un grafo tenemos dos estrategias: BFS (Breadth First Search) que explora por "capas" o niveles usando una Cola; y DFS (Depth First Search) que se adentra lo más posible en una rama antes de retroceder, usando una Pila (o recursividad).

Imagen 52. Comparación de rutas de recorrido BFS y DFS
Orden de visita: Diagrama numerado mostrando el orden en que se visitan los nodos. BFS prioriza vecinos cercanos, DFS prioriza caminos largos.
Implementaremos un Grafo usando Listas de Adyacencia, ya que es la forma más versátil. Usaremos un struct "Grafo" que contiene un array de punteros. Cada puntero es la cabeza de una lista enlazada que contiene los vecinos de ese vértice.

Imagen 53. Implementación de Lista de Adyacencia en C
Diagrama de implementación: Muestra el "Array Principal" (índices 0 a N) donde de cada celda cuelga una lista enlazada de vecinos.