Implementación de Estructuras de Datos de Grafos en Python
Clasificado en Informática
Escrito el en español con un tamaño de 7,73 KB
Este documento detalla la implementación de una estructura de datos de grafo en Python, incluyendo la definición de nodos, la gestión de conexiones y la aplicación de algoritmos para encontrar caminos y optimizar costos.
Clase `NodoGrafo`
Representa un nodo individual dentro del grafo.
- `__init__(self, valor)`: Constructor de la clase `NodoGrafo`. Inicializa el nodo con un
valor
y una lista vacía para almacenar susconexiones
. Cada conexión es una tupla que contiene el nodo de destino y una etiqueta (que puede incluir información como el costo). - `agregar_conexion(self, nodo_destino, etiqueta)`: Añade una conexión a este nodo. Verifica que no exista ya una conexión al mismo nodo de destino para evitar duplicados.
Clase `Grafo`
Representa la estructura completa del grafo, gestionando todos los nodos y sus interconexiones.
- `__init__(self)`: Constructor de la clase `Grafo`. Inicializa un diccionario vacío
self.nodos
para almacenar los nodos, donde la clave es el valor del nodo y el valor es la instancia del objeto `NodoGrafo`. - `agregar_nodo(self, valor)`: Añade un nuevo nodo al grafo si este no existe previamente.
- `agregar_arista(self, origen, destino, etiqueta)`: Crea una conexión (arista) entre dos nodos existentes. Añade la conexión en ambas direcciones para representar un grafo no dirigido.
Funcionalidades de Búsqueda y Optimización
- `nodo_con_mas_conexiones(self)`: Itera sobre todos los nodos del grafo y devuelve el valor del nodo que posee la mayor cantidad de conexiones salientes.
- `conexion_mas_costosa(self)`: Calcula el costo total de las conexiones entre nodos adyacentes (considerando el costo de la conexión de ida y la de vuelta, asumiendo que la etiqueta contiene un diccionario con la clave 'cost'). Devuelve la tupla de nodos que forman la conexión más costosa y su costo total.
- `camino_minimo_costo(self, inicio, fin)`: Implementa el algoritmo de Dijkstra para encontrar el camino de menor costo entre dos nodos especificados (
inicio
yfin
). Utiliza una cola de prioridad (`heapq`) para gestionar eficientemente los nodos a visitar. Devuelve el camino como una lista de nodos y el costo acumulado.
Ejemplo de Uso
A continuación, se muestra un ejemplo práctico de cómo utilizar la clase `Grafo`:
import heapq
class NodoGrafo:
def __init__(self, valor):
self.valor = valor
self.conexiones = [] # Lista de tuplas (nodo_destino, etiqueta)
def agregar_conexion(self, nodo_destino, etiqueta):
if all(conn[0] != nodo_destino for conn in self.conexiones):
self.conexiones.append((nodo_destino, etiqueta))
class Grafo:
def __init__(self):
self.nodos = {}
def agregar_nodo(self, valor):
if valor not in self.nodos:
self.nodos[valor] = NodoGrafo(valor)
def agregar_arista(self, origen, destino, etiqueta):
if origen in self.nodos and destino in self.nodos:
self.nodos[origen].agregar_conexion(destino, etiqueta)
self.nodos[destino].agregar_conexion(origen, etiqueta)
def nodo_con_mas_conexiones(self):
max_conexiones = 0
nodo_max = None
for nodo in self.nodos.values():
if len(nodo.conexiones) > max_conexiones:
max_conexiones = len(nodo.conexiones)
nodo_max = nodo.valor
return nodo_max
def conexion_mas_costosa(self):
max_costo = 0
conexion_max = None
for nodo in self.nodos.values():
for conexion in nodo.conexiones:
# Asumiendo que la etiqueta es un diccionario con 'cost'
# y que la conexión de destino también tiene una conexión con 'cost'
# Nota: Esta lógica puede necesitar ajuste dependiendo de la estructura exacta de la etiqueta
try:
costo_origen = conexion[1]['cost']
# Intentar acceder al costo de la conexión de vuelta para el cálculo
# Esto asume que el nodo de destino tiene al menos una conexión y la primera es la relevante
if self.nodos[conexion[0]].conexiones:
costo_destino = self.nodos[conexion[0]].conexiones[0][1]['cost']
costo_total = costo_origen + costo_destino
if costo_total > max_costo:
max_costo = costo_total
conexion_max = (nodo.valor, conexion[0])
else:
# Si el nodo de destino no tiene conexiones, solo consideramos el costo de ida
if costo_origen > max_costo:
max_costo = costo_origen
conexion_max = (nodo.valor, conexion[0])
except (KeyError, IndexError):
# Manejar casos donde 'cost' no está presente o la lista de conexiones está vacía
pass
return conexion_max, max_costo
def camino_minimo_costo(self, inicio, fin):
import heapq
cola_prioridad = [(0, inicio, [])]
visitados = set()
while cola_prioridad:
acumulado, actual, camino = heapq.heappop(cola_prioridad)
if actual in visitados:
continue
visitados.add(actual)
camino = camino + [actual]
if actual == fin:
return camino, acumulado
if actual not in self.nodos: # Asegurarse de que el nodo actual existe
continue
for vecino, etiqueta in self.nodos[actual].conexiones:
if vecino not in visitados:
try:
costo = etiqueta['cost']
heapq.heappush(cola_prioridad, (acumulado + costo, vecino, camino))
except KeyError:
# Manejar el caso donde la etiqueta no tiene 'cost'
pass
return None, None
# Creación del grafo y adición de nodos y aristas
grafo = Grafo()
camaras = [1, 2, 3, 4, 5]
for camara in camaras:
grafo.agregar_nodo(camara)
# Crear conexiones ficticias y costos
conexiones = [(1, 2, 10), (1, 3, 15), (2, 4, 20), (2, 5, 10), (3, 4, 5), (3, 5, 10), (4, 5, 5)]
for origen, destino, costo in conexiones:
grafo.agregar_arista(origen, destino, {'cost': costo})
# Obtener respuestas
nodo_max = grafo.nodo_con_mas_conexiones()
conexion_max, costo_max = grafo.conexion_mas_costosa()
camino, costo = grafo.camino_minimo_costo(1, 4)
# Imprimir resultados
print(f"La cámara con más conexiones es: Cámara {nodo_max}")
print(f"La conexión más costosa es entre la Cámara {conexion_max[0]} y la Cámara {conexion_max[1]} con un costo de {costo_max}")
print(f"El camino de menor costo desde la Cámara 1 a la Cámara 4 es: {camino} con un costo de {costo}")
Resultados del Ejemplo
La ejecución del código anterior producirá las siguientes salidas:
La cámara con más conexiones es: Cámara 2
La conexión más costosa es entre la Cámara 2 y la Cámara 4 con un costo de 30
El camino de menor costo desde la Cámara 1 a la Cámara 4 es: [1, 3, 4] con un costo de 20
Nota: El cálculo de la conexion_mas_costosa
en el ejemplo original podría tener una lógica simplificada. La implementación proporcionada intenta considerar el costo de ida y vuelta, pero la interpretación exacta de 'costo total' puede variar según los requisitos específicos del problema.