Loading...

🔧 Manual Técnico - Gestor Aeropuerto++

Tabla de Contenidos

  1. Instalación Completa
  2. Configuración del Entorno
  3. Sistema de Compilación (Makefile)
  4. Parser JSON (nlohmann/json)
  5. Arquitectura del Proyecto
  6. Estructuras de Datos
  7. Algoritmos Principales
  8. Compilación y Debugging

Instalación Completa

Requisitos del Sistema

  • SO: Windows 10+ con MSYS64/MinGW
  • Arquitectura: 64-bit (x86_64)
  • RAM: Mínimo 2GB
  • Espacio: 500MB para herramientas

Paso 1: Instalar MSYS64

MSYS64 proporciona el compilador GCC, Make y las herramientas necesarias.

Descarga

  1. Visita https://www.msys2.org/
  2. Descarga el instalador msys2-x86_64-...exe
  3. Ejecuta el instalador
  4. Sigue los pasos por defecto (instala en C:\msys64)

Configuración Inicial

Abre MSYS2 MinGW 64-bit (busca en Start Menu):

1
pacman -Syu

Esto actualiza la base de datos de paquetes. Si se cierra la ventana, reabre y ejecuta nuevamente:

1
pacman -Su

Paso 2: Instalar GCC y Herramientas

En MSYS2 MinGW 64-bit:

1
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-make

Responde Y cuando se pida confirmación.

Verifica la instalación:

1
g++ --version

Salida esperada:

1
g++ (Rev5, Built by MSYS2 project) 15.2.0

Paso 3: Instalar Graphviz

En MSYS2 MinGW 64-bit:

1
pacman -S mingw-w64-x86_64-graphviz

Verifica:

1
dot --version

Salida esperada:

1
dot - graphviz version 12.x.x

Paso 4: Configurar Variables de Entorno (Opcional)

Para acceder a g++, make y dot desde cualquier terminal:

  1. Abre Variables de entorno (busca en Control Panel)
  2. Click en Editar variables de entorno del sistema
  3. Click en Variables de entorno…
  4. En Variables del sistema, selecciona Path y click Editar
  5. Añade las siguientes rutas:
    1
    2
    C:\msys64\ucrt64\bin
    C:\msys64\usr\bin
  6. Click OK y reinicia el terminal

Paso 5: Configurar el Proyecto

Clona o descarga el repositorio:

1
2
git clone <repositorio>
cd 201318644_EDD_Practica

Verifica que exista la estructura:

1
2
ls -la
# Debería mostrar: makefile, src/, include/, data/, etc.

Configuración del Entorno

Estructura de Directorios Esperada

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
C:\Users\[usuario]\Documents\USAC\201318644_EDD_Practica\
├── include/
│ ├── models/
│ │ ├── Avion.h
│ │ ├── Pasajero.h
│ │ └── Equipaje.h
│ ├── nodes/
│ │ ├── NodoAvion.h
│ │ ├── NodoPasajero.h
│ │ └── NodoEquipaje.h
│ ├── structures/
│ │ ├── ListaCircularDoble.h
│ │ ├── ListaDoble.h
│ │ ├── Cola.h
│ │ └── Pila.h
│ └── utils/
│ ├── JsonParser.h
│ ├── Graphviz.h
│ └── json.hpp
├── src/
│ ├── main.cpp
│ ├── models/
│ ├── nodes/
│ ├── structures/
│ └── utils/
├── data/
│ ├── aviones.json
│ ├── pasajeros.json
│ └── Cambios.txt
├── reports/
└── makefile

Verificar la Instalación

Abre PowerShell (como administrador):

1
2
3
g++ --version
make --version
dot --version

Si todos los comandos funcionan, estás listo.


Sistema de Compilación (Makefile)

Qué es un Makefile

Un Makefile es un archivo de automatización que:

  • Define las reglas de compilación
  • Gestiona dependencias
  • Simplifica el proceso de build

Análisis del Makefile del Proyecto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# Compilador a usar
CXX = g++

# Flags de compilación
CXXFLAGS = -I include/ -Wall -g

# Nombre del ejecutable de salida
TARGET = aeropuerto.exe

# Lista de archivos fuente
SOURCES = src/main.cpp \
src/models/Avion.cpp \
src/models/Pasajero.cpp \
src/models/Equipaje.cpp \
src/nodes/NodoAvion.cpp \
src/nodes/NodoPasajero.cpp \
src/nodes/NodoEquipaje.cpp \
src/structures/ListaCircularDoble.cpp \
src/structures/ListaDoble.cpp \
src/structures/Cola.cpp \
src/structures/Pila.cpp \
src/utils/JsonParser.cpp \
src/utils/Graphviz.cpp

# Regla por defecto (hacer todo)
all: $(TARGET)

# Regla para generar el ejecutable
$(TARGET): $(SOURCES)
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SOURCES)

# Regla para limpiar archivos compilados
clean:
del /F $(TARGET) 2>nul || echo "Nothing to clean"

# Regla para compilar y ejecutar
run: $(TARGET)
$(TARGET)

Explicación de Flags

Flag Significado Uso
-I include/ Include path Busca headers en carpeta include/
-Wall Show all warnings Muestra advertencias de compilación
-g Debug symbols Incluye información para debugger
-o Output file Especifica nombre del ejecutable

Comandos Make Disponibles

1
2
3
4
5
6
7
8
9
10
11
# Compilar (por defecto)
make

# Compilar y ejecutar
make run

# Limpiar archivos compilados
make clean

# Limpiar y recompilar
make clean && make

Cómo Funciona la Compilación

  1. Análisis de dependencias: Make verifica si algún .cpp o .h cambió
  2. Compilación: Invoca g++ con los flags especificados
  3. Linking: Enlaza todos los archivos objeto
  4. Generación del ejecutable: Crea aeropuerto.exe

Parser JSON (nlohmann/json)

¿Por Qué nlohmann/json?

  • Single-header: Un solo archivo (json.hpp)
  • Modern C++: Sintaxis limpia y moderna
  • Sin dependencias: No requiere librerías externas
  • Mantenido activamente: Comunidad grande
  • Rendimiento: Optimizado para velocidad

Ubicación e Instalación

El archivo json.hpp ya está incluido en:

1
include/utils/json.hpp

Línea de 25,713 líneas que proporciona toda la funcionalidad JSON.

Cómo Se Usa en el Proyecto

1. Incluir el Header

1
2
3
#include "json.hpp"

using json = nlohmann::json; // Alias para comodidad

2. Cargar desde Archivo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Abrir archivo
std::ifstream archivo("data/aviones.json");

// Parsear JSON
json datos = json::parse(archivo);

// Iterar sobre array
for (const auto& item : datos) {
std::string vuelo = item["vuelo"];
std::string registro = item["numero_de_registro"];
int capacidad = item["capacidad"];
}

archivo.close();

Ejemplo Práctico: Cargar Aviones

Archivo JSON (data/aviones.json)

1
2
3
4
5
6
7
8
9
10
11
12
13
[
{
"vuelo": "A100",
"numero_de_registro": "N12345",
"modelo": "Boeing 737",
"fabricante": "Boeing",
"ano_fabricacion": 2015,
"capacidad": 180,
"peso_max_despegue": 79000,
"aerolinea": "AirlineX",
"estado": "Disponible"
}
]

Código (src/utils/JsonParser.cpp)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void JsonParser::cargarAviones(std::string rutaArchivo,
ListaCircularDoble* listaDisponibles,
ListaCircularDoble* listaMantenimiento) {
try {
// 1. Abrir archivo
std::ifstream archivo(rutaArchivo);
if (!archivo.is_open()) {
std::cout << "Error al abrir archivo: " << rutaArchivo << std::endl;
return;
}

// 2. Parsear JSON
json datos = json::parse(archivo);

// 3. Iterar sobre cada avión
for (const auto& avion_data : datos) {
// 4. Crear objeto Avion desde JSON
Avion* avion = new Avion(
avion_data["vuelo"],
avion_data["numero_de_registro"],
avion_data["modelo"],
avion_data["fabricante"],
avion_data["ano_fabricacion"],
avion_data["capacidad"],
avion_data["peso_max_despegue"],
avion_data["aerolinea"],
avion_data["estado"]
);

// 5. Insertar en lista según estado
std::string estado = avion_data["estado"];
if (estado == "Disponible") {
listaDisponibles->insertar(avion);
} else if (estado == "Mantenimiento") {
listaMantenimiento->insertar(avion);
}
}

archivo.close();
std::cout << "Aviones cargados correctamente." << std::endl;
}
catch (const std::exception& e) {
// Manejo de errores JSON
std::cout << "Error al parsear JSON: " << e.what() << std::endl;
}
}

Manejo de Errores

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try {
json datos = json::parse(archivo);
// ... procesar datos
}
catch (const json::parse_error& e) {
std::cerr << "Parse error: " << e.what() << std::endl;
std::cerr << "At byte: " << e.byte << std::endl;
}
catch (const json::exception& e) {
std::cerr << "JSON error: " << e.what() << std::endl;
}
catch (const std::exception& e) {
std::cerr << "General error: " << e.what() << std::endl;
}

Arquitectura del Proyecto

Capas de la Arquitectura

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌─────────────────────────────────────────┐
│ PRESENTACIÓN (main.cpp) │
│ - Menú interactivo │
│ - Entrada/salida de usuario │
└──────────────────┬──────────────────────┘

┌──────────────────▼──────────────────────┐
│ LÓGICA DE NEGOCIO │
│ - JsonParser: Carga de datos │
│ - Graphviz: Generación de reportes │
│ - Controllers: Procesamiento │
└──────────────────┬──────────────────────┘

┌──────────────────▼──────────────────────┐
│ MODELOS DE DATOS │
│ - Avion, Pasajero, Equipaje │
│ - Encapsulación de objetos │
└──────────────────┬──────────────────────┘

┌──────────────────▼──────────────────────┐
│ ESTRUCTURAS DE DATOS │
│ - ListaCircularDoble (Aviones) │
│ - Cola (Pasajeros en registro) │
│ - Pila (Equipaje) │
│ - ListaDoble (Pasajeros ordenados) │
└──────────────────┬──────────────────────┘

┌──────────────────▼──────────────────────┐
│ NODOS GENÉRICOS │
│ - NodoAvion, NodoPasajero, Nodo... │
│ - Contienen datos y referencias │
└─────────────────────────────────────────┘

Flujo de Datos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
JSON File


JsonParser::parse()

├─→ Avion objects ──→ ListaCircularDoble

└─→ Pasajero objects ──→ Cola

Procesamiento (main):

├─→ Desencolar(Cola) ──→ ListaDoble::insertarOrdenado()

└─→ Equipaje ──→ Pila::push()

Reportes:

├─→ generarGraphviz() ──→ DOT code

└─→ Graphviz::generar() ──→ PNG image

Estructuras de Datos

1. Lista Circular Doble (Aviones)

Características

  • Tipo: Circular, doblemente enlazada
  • Uso: Almacenar aviones disponibles y en mantenimiento
  • Ventaja: Permite iteración bidireccional y circular

Estructura de Nodo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class NodoAvion {
private:
Avion* avion;
NodoAvion* siguiente;
NodoAvion* anterior;
public:
NodoAvion(Avion* avion);
~NodoAvion();
Avion* getAvion();
void setAvion(Avion* avion);
NodoAvion* getSiguiente();
NodoAvion* getAnterior();
// ... setters
};

Constructor

1
2
3
4
ListaCircularDoble::ListaCircularDoble() {
this->primero = nullptr; // Sin elementos inicialmente
this->tamanio = 0;
}

Inserción

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void ListaCircularDoble::insertar(Avion* avion) {
// 1. Crear nuevo nodo
NodoAvion* nuevoNodo = new NodoAvion(avion);

if (estaVacia()) {
// Caso 1: Primera inserción
primero = nuevoNodo;
nuevoNodo->setSiguiente(nuevoNodo); // Apunta a sí mismo
nuevoNodo->setAnterior(nuevoNodo); // Apunta a sí mismo
} else {
// Caso 2: Inserción en lista no vacía
NodoAvion* ultimo = primero->getAnterior(); // Obtener último

// Conectar nuevo nodo
ultimo->setSiguiente(nuevoNodo);
nuevoNodo->setAnterior(ultimo);
nuevoNodo->setSiguiente(primero);
primero->setAnterior(nuevoNodo);
}
tamanio++;
}

/*
VISUALIZACIÓN:
Antes: [A] <---> [A] (circular)
Después: [A] <---> [B] <---> [A] (circular)
*/

Búsqueda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Avion* ListaCircularDoble::buscar(std::string numeroRegistro) {
if (estaVacia()) return nullptr;

NodoAvion* actual = primero;

// Recorrer hasta volver al inicio
do {
if (actual->getAvion()->getNumeroRegistro() == numeroRegistro) {
return actual->getAvion();
}
actual = actual->getSiguiente();
} while (actual != primero); // Detener cuando vuelva al inicio

return nullptr;
}

// Complejidad: O(n) - puede ser todo el círculo

Eliminación

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Avion* ListaCircularDoble::eliminar(std::string numeroRegistro) {
if (estaVacia()) return nullptr;

NodoAvion* actual = primero;

do {
if (actual->getAvion()->getNumeroRegistro() == numeroRegistro) {
// Encontrado - extraer objeto
Avion* avionEliminado = actual->getAvion();
actual->setAvion(nullptr); // Prevenir eliminación doble

if (tamanio == 1) {
// Último elemento
primero = nullptr;
} else {
// Obtener vecinos
NodoAvion* anterior = actual->getAnterior();
NodoAvion* siguiente = actual->getSiguiente();

// Reconectar
anterior->setSiguiente(siguiente);
siguiente->setAnterior(anterior);

// Actualizar primero si es necesario
if (actual == primero) {
primero = siguiente;
}
}

delete actual; // Liberar memoria del nodo
tamanio--;
return avionEliminado;
}
actual = actual->getSiguiente();
} while (actual != primero);

return nullptr;
}

/*
VISUALIZACIÓN:
Antes: [A] <---> [B] <---> [C] <---> [A]
(eliminar B)
Después: [A] <---> [C] <---> [A]
*/

2. Cola FIFO (Pasajeros)

Características

  • Tipo: Cola (First In, First Out)
  • Uso: Registro de pasajeros
  • Operaciones: Encolar, desencolar

Estructura

1
2
3
4
5
6
class Cola {
private:
NodoPasajero* frente; // Primero en entrar
NodoPasajero* fin; // Último en entrar
int tamanio;
};

Encolar (Agregar al Final)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Cola::encolar(Pasajero* pasajero) {
// 1. Crear nuevo nodo
NodoPasajero* nuevoNodo = new NodoPasajero(pasajero);

if (estaVacia()) {
// Primer elemento
frente = fin = nuevoNodo;
} else {
// Agregar al final
fin->setSiguiente(nuevoNodo);
nuevoNodo->setAnterior(fin);
fin = nuevoNodo;
}
tamanio++;
}

/*
VISUALIZACIÓN FIFO:
Encolar (A): FRENTE [A] FIN
Encolar (B): FRENTE [A] <-> [B] FIN
Encolar (C): FRENTE [A] <-> [B] <-> [C] FIN
*/

Desencolar (Extraer del Frente)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Pasajero* Cola::desencolar() {
if (estaVacia()) {
std::cout << "Cola vacía" << std::endl;
return nullptr;
}

// 1. Obtener primero
NodoPasajero* temp = frente;
Pasajero* pasajero = temp->getPasajero();
temp->setPasajero(nullptr); // Prevenir eliminación doble

// 2. Mover frente
frente = frente->getSiguiente();

if (frente == nullptr) {
// La cola quedó vacía
fin = nullptr;
} else {
// El nuevo frente no tiene anterior
frente->setAnterior(nullptr);
}

// 3. Liberar nodo
delete temp;
tamanio--;
return pasajero;
}

/*
VISUALIZACIÓN FIFO:
Antes: FRENTE [A] <-> [B] <-> [C] FIN
Desencolar(): FRENTE [B] <-> [C] FIN (retorna A)
*/

3. Pila LIFO (Equipaje)

Características

  • Tipo: Pila (Last In, First Out)
  • Uso: Almacenamiento de equipaje
  • Operaciones: Push, pop

Estructura

1
2
3
4
5
class Pila {
private:
NodoEquipaje* cima; // Tope de la pila
int tamanio;
};

Push (Agregar a la Cima)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void Pila::push(Equipaje* equipaje) {
// 1. Crear nuevo nodo
NodoEquipaje* nuevoNodo = new NodoEquipaje(equipaje);

// 2. El nuevo nodo apunta a la cima actual
nuevoNodo->setSiguiente(cima);

// 3. El nuevo nodo es la nueva cima
cima = nuevoNodo;
tamanio++;
}

/*
VISUALIZACIÓN LIFO:
Push (A): CIMA

[A] BASE

Push (B): CIMA

[B]

[A] BASE
*/

Pop (Extraer de la Cima)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Equipaje* Pila::pop() {
if (estaVacia()) {
std::cout << "Pila vacía" << std::endl;
return nullptr;
}

// 1. Obtener equipaje de la cima
NodoEquipaje* temp = cima;
Equipaje* equipaje = temp->getEquipaje();
temp->setEquipaje(nullptr); // Prevenir eliminación doble

// 2. Mover cima
cima = cima->getSiguiente();

// 3. Liberar nodo
delete temp;
tamanio--;
return equipaje;
}

/*
VISUALIZACIÓN LIFO:
Antes: CIMA

[B]

[A] BASE

Pop(): retorna B
CIMA

[A] BASE
*/

4. Lista Doble Ordenada (Pasajeros)

Características

  • Tipo: Lista doblemente enlazada, ordenada
  • Uso: Pasajeros registrados (ordenados por vuelo y asiento)
  • Ordenamiento: Primero por vuelo, luego por asiento

Inserción Ordenada

Este es el algoritmo más importante. Mantiene el orden automáticamente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
void ListaDoble::insertarOrdenado(Pasajero* pasajero) {
// 1. Crear nuevo nodo
NodoPasajero* nuevoNodo = new NodoPasajero(pasajero);

if (estaVacia()) {
// Caso 1: Primera inserción
primero = ultimo = nuevoNodo;
} else {
// Caso 2: Buscar posición ordenada
NodoPasajero* actual = primero;

// BÚSQUEDA: Recorrer hasta encontrar la posición correcta
while (actual != nullptr) {
// Comparación 1: Por número de vuelo
if (pasajero->getVuelo() < actual->getPasajero()->getVuelo()) {
// El nuevo pasajero va antes
break;
}
// Comparación 2: Si mismo vuelo, comparar por asiento
else if (pasajero->getVuelo() == actual->getPasajero()->getVuelo()) {
if (pasajero->getAsiento() < actual->getPasajero()->getAsiento()) {
// El nuevo pasajero va antes
break;
}
}
// Continuar buscando
actual = actual->getSiguiente();
}

// INSERCIÓN en la posición encontrada
if (actual == primero) {
// Caso 2a: Insertar al INICIO
nuevoNodo->setSiguiente(primero);
primero->setAnterior(nuevoNodo);
primero = nuevoNodo;
}
else if (actual == nullptr) {
// Caso 2b: Insertar al FINAL
ultimo->setSiguiente(nuevoNodo);
nuevoNodo->setAnterior(ultimo);
ultimo = nuevoNodo;
}
else {
// Caso 2c: Insertar en MEDIO
NodoPasajero* anterior = actual->getAnterior();
anterior->setSiguiente(nuevoNodo);
nuevoNodo->setAnterior(anterior);
nuevoNodo->setSiguiente(actual);
actual->setAnterior(nuevoNodo);
}
}
tamanio++;
}

/*
EJEMPLO DE INSERCIÓN ORDENADA:

Insertar: (John, A100, 12)
Lista: vacía
Resultado: [John: A100-12]

Insertar: (Jane, A200, 5)
Lista: [John: A100-12]
Resultado: [John: A100-12] <-> [Jane: A200-5]

Insertar: (Carlos, A100, 15)
Lista: [John: A100-12] <-> [Jane: A200-5]
Búsqueda: A100 < A200, y 15 > 12, así que va después de John
Resultado: [John: A100-12] <-> [Carlos: A100-15] <-> [Jane: A200-5]

Insertar: (Pedro, A100, 8)
Lista: [John: A100-12] <-> [Carlos: A100-15] <-> [Jane: A200-5]
Búsqueda: A100 == A100, pero 8 < 12, así que va antes de John
Resultado: [Pedro: A100-8] <-> [John: A100-12] <-> [Carlos: A100-15] <-> [Jane: A200-5]
*/

Complejidad del Algoritmo

1
2
3
Búsqueda:   O(n) - peor caso: recorre toda la lista
Inserción: O(1) - una vez encontrada la posición
Total: O(n) - dominado por la búsqueda

Algoritmos Principales

1. Algoritmo de Ordenamiento por Inserción (Inserción Ordenada)

Tipo: Ordenamiento por inserción
Complejidad: O(n) en búsqueda + O(1) en inserción
Estabilidad: Estable (mantiene orden relativo)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// PSEUDOCÓDIGO
function insertarOrdenado(pasajero):
nuevoNodo = crearNodo(pasajero)

if lista vacía:
primero = nuevoNodo
return

// Búsqueda lineal
actual = primero
while actual != NULL:
if pasajero < actual: // Comparación (vuelo, luego asiento)
break
actual = siguiente

// Inserción en posición encontrada
if actual == primero:
insertarAlInicio(nuevoNodo)
else if actual == NULL:
insertarAlFinal(nuevoNodo)
else:
insertarEnMedio(nuevoNodo, actual)

Ventajas:

  • ✓ Lista siempre ordenada
  • ✓ O(1) para inserción en posición
  • ✓ Eficiente para datos insertados ordenadamente

Desventajas:

  • ✗ O(n) búsqueda lineal
  • ✗ No óptimo para búsquedas

Mejora posible: Usar búsqueda binaria → O(log n)

2. Algoritmo de Búsqueda Lineal

Tipo: Búsqueda secuencial
Complejidad: O(n)
Uso: ListaDoble, ListaCircularDoble

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// En ListaDoble
Pasajero* ListaDoble::buscar(std::string numeroPasaporte) {
NodoPasajero* actual = primero;

// Recorrer linealmente
while (actual != nullptr) {
if (actual->getPasajero()->getNumeroPasaporte() == numeroPasaporte) {
return actual->getPasajero(); // Encontrado
}
actual = actual->getSiguiente();
}

return nullptr; // No encontrado
}

// Complejidad:
// Mejor caso: O(1) - encontrado al inicio
// Peor caso: O(n) - encontrado al final o no existe
// Promedio: O(n/2) ≈ O(n)

Mejora posible: Si la lista está ordenada por pasaporte, usar búsqueda binaria.

3. Algoritmo de Búsqueda Circular

Tipo: Búsqueda en estructura circular
Complejidad: O(n)
Uso: ListaCircularDoble

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Avion* ListaCircularDoble::buscar(std::string numeroRegistro) {
if (estaVacia()) return nullptr;

NodoAvion* actual = primero;

// DO-WHILE: Garantiza visitar todos los nodos
do {
if (actual->getAvion()->getNumeroRegistro() == numeroRegistro) {
return actual->getAvion();
}
actual = actual->getSiguiente();
} while (actual != primero); // Detener al volver al inicio

return nullptr;
}

// Clave: La condición "actual != primero" detecta cuando
// se volvió al inicio después de recorrer todo el círculo

4. Algoritmo de Eliminación Circular

Tipo: Eliminación en lista circular
Complejidad: O(n) búsqueda + O(1) eliminación
Dificultad: Manejar el caso especial del primero

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Avion* ListaCircularDoble::eliminar(std::string numeroRegistro) {
if (estaVacia()) return nullptr;

NodoAvion* actual = primero;

do {
if (actual->getAvion()->getNumeroRegistro() == numeroRegistro) {
Avion* avionEliminado = actual->getAvion();
actual->setAvion(nullptr);

if (tamanio == 1) {
// CASO ESPECIAL: Único elemento
primero = nullptr;
} else {
// Obtener vecinos
NodoAvion* anterior = actual->getAnterior();
NodoAvion* siguiente = actual->getSiguiente();

// Desconectar el nodo
anterior->setSiguiente(siguiente);
siguiente->setAnterior(anterior);

// CASO ESPECIAL: Si eliminamos primero, actualizar
if (actual == primero) {
primero = siguiente;
}
}

delete actual;
tamanio--;
return avionEliminado;
}
actual = actual->getSiguiente();
} while (actual != primero);

return nullptr;
}

/*
VISUALIZACIÓN ELIMINACIÓN:

Caso 1: Un solo elemento
Antes: [A] <-> [A] (circular)
Después: NULL

Caso 2: Elemento al inicio
Antes: [A] <-> [B] <-> [C] <-> [A]
Eliminar A:
Después: [B] <-> [C] <-> [B]
primero = B (actualizar)

Caso 3: Elemento en medio
Antes: [A] <-> [B] <-> [C] <-> [A]
Eliminar B:
Después: [A] <-> [C] <-> [A]

Caso 4: Elemento al final (mismo que 3 en circular)
*/

5. Algoritmo de Gestión de Memoria

Tipo: Recorrido con liberación
Complejidad: O(n)
Uso: Destructores

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Destructor de ListaCircularDoble
ListaCircularDoble::~ListaCircularDoble() {
if (!estaVacia()) {
NodoAvion* actual = primero;
NodoAvion* siguiente;

// DO-WHILE: Visitar todos los nodos en círculo
do {
siguiente = actual->getSiguiente();
delete actual; // Liberar memoria
actual = siguiente;
} while (actual != primero); // Detener al volver al inicio
}
}

// Clave: Guardar "siguiente" ANTES de delete, porque
// después de delete el puntero es inválido

6. Algoritmo de Procesamiento de Movimientos

Tipo: Parsing y procesamiento de comandos
Complejidad: O(m * n) - m líneas, n búsqueda
Ubicación: main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
void procesarMovimientos() {
std::string ruta;
std::cout << "Ingrese la ruta del archivo de movimientos: ";
std::cin >> ruta;

std::ifstream archivo(ruta);
std::string linea;

if (!archivo.is_open()) {
std::cout << "Error al abrir el archivo" << std::endl;
return;
}

// Leer línea por línea
while (getline(archivo, linea)) {

// TIPO 1: IngresoEquipajes
if (linea.find("IngresoEquipajes") != std::string::npos) {
// Desencolar pasajero de la cola
Pasajero* pasajero = colaRegistro->desencolar();

if (pasajero != nullptr) {
// Insertar en lista ordenada
listaPasajeros->insertarOrdenado(pasajero);

// Si tiene equipaje, agregarlo a la pila
if (pasajero->getEquipajeFacturado() > 0) {
Equipaje* equipaje = new Equipaje(
pasajero->getNumeroPasaporte(),
pasajero->getEquipajeFacturado()
);
pilaEquipaje->push(equipaje);
}
}
}

// TIPO 2: MantenimientoAviones
else if (linea.find("MantenimientoAviones") != std::string::npos) {
// Parsear: MantenimientoAviones,Acción,NumeroRegistro
size_t pos1 = linea.find(",");
size_t pos2 = linea.find(",", pos1 + 1);

std::string accion = linea.substr(pos1 + 1, pos2 - pos1 - 1);
std::string numeroRegistro = linea.substr(pos2 + 1);

// Limpiar espacios y caracteres especiales
while (!numeroRegistro.empty() &&
(numeroRegistro.back() == '\r' ||
numeroRegistro.back() == '\n' ||
numeroRegistro.back() == ' ' ||
numeroRegistro.back() == ';')) {
numeroRegistro.pop_back();
}

if (accion == "Ingreso") {
// Mover de disponibles a mantenimiento
Avion* avion = avionesDisponibles->eliminar(numeroRegistro);
if (avion != nullptr) {
avion->setEstado("Mantenimiento");
avionesMantenimiento->insertar(avion);
std::cout << "Avion " << numeroRegistro
<< " movido a mantenimiento." << std::endl;
}
}
else if (accion == "Salida") {
// Mover de mantenimiento a disponibles
Avion* avion = avionesMantenimiento->eliminar(numeroRegistro);
if (avion != nullptr) {
avion->setEstado("Disponible");
avionesDisponibles->insertar(avion);
std::cout << "Avion " << numeroRegistro
<< " movido a disponibles." << std::endl;
}
}
}
}

archivo.close();
std::cout << "Movimientos procesados correctamente." << std::endl;
}

/*
FLUJO:
Leer línea → Identificar tipo → Parsear parámetros → Ejecutar operación

Complejidad:
- Leer m líneas: O(m)
- Cada operación (desencolar, buscar, insertar): O(n)
- Total: O(m * n)
*/

Compilación y Debugging

Proceso de Compilación Detallado

1. Compilación Simple

1
make

Ejecuta:

1
g++ -I include/ -Wall -g -o aeropuerto.exe src/main.cpp src/models/... src/utils/...

Pasos internos:

  1. Preprocesamiento (#include, #define)
  2. Compilación (código fuente → código máquina)
  3. Linking (enlazar símbolos externos)
  4. Generación del ejecutable

2. Compilación con Errores

Si hay errores de sintaxis:

1
make 2>&1 | tee compile.log

Esto guarda los errores en compile.log.

3. Limpiar y Recompilar

1
make clean && make

Útil cuando hay cambios en headers.

Errores Comunes y Soluciones

Error: No such file or directory

1
src/main.cpp:5:20: fatal error: models/Avion.h: No such file or directory

Solución:

1
2
3
4
5
6
# Verificar estructura de directorios
ls -R include/
ls -R src/

# El header debe estar en include/ y ser incluido como:
#include "models/Avion.h" // Con comillas

Error: undefined reference

1
undefined reference to `Avion::Avion(...)'

Causa: Archivo .cpp no compilado o falta de implementación

Solución:

  1. Verificar que src/models/Avion.cpp existe
  2. Verificar que está en SOURCES en el makefile
  3. Verificar que la implementación coincide con el header

Error: JSON parse error

1
2
3
4
5
try {
json datos = json::parse(archivo);
} catch (const json::parse_error& e) {
std::cerr << "Parse error at byte " << e.byte << ": " << e.what() << std::endl;
}

Solución:

  • Validar JSON con herramienta online
  • Verificar codificación del archivo (debe ser UTF-8)
  • Verificar que no hay caracteres especiales

Debugging con GDB

El flag -g en el makefile incluye símbolos de debug.

1
2
3
4
5
6
7
8
9
10
11
12
# Iniciar debugger
gdb aeropuerto.exe

# Comandos útiles
(gdb) break main # Punto de quiebre en main
(gdb) break Avion::insertar # Punto de quiebre en método
(gdb) run # Ejecutar
(gdb) next # Siguiente línea
(gdb) step # Entrar en función
(gdb) print variable_name # Mostrar valor
(gdb) backtrace # Ver pila de llamadas
(gdb) quit # Salir

Optimizaciones de Compilación

Para versión de producción:

1
2
3
4
5
# Agregar flags de optimización
CXXFLAGS = -I include/ -O2 -Wall

# O nivel máximo (más lento de compilar, más rápido de ejecutar)
CXXFLAGS = -I include/ -O3 -Wall

Notas de Implementación

Gestión de Memoria

Regla importante: Cada new debe tener un delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ✓ CORRECTO
Avion* avion = new Avion(...);
// ... usar avion
delete avion;

// ✗ INCORRECTO (memory leak)
Avion* avion = new Avion(...);
return; // Sin delete

// ✓ CORRECTO (en destructor)
ListaCircularDoble::~ListaCircularDoble() {
while (!estaVacia()) {
Avion* avion = eliminar(...);
delete avion;
}
}

Punteros Nulos

1
2
3
4
5
6
7
// ✓ Verificar antes de usar
if (puntero != nullptr) {
// Seguro usar puntero
}

// ✗ Peligroso
puntero->metodo(); // Si es nullptr, crash

Strings en C++

1
2
3
4
5
6
7
// std::string vs const char*
std::string s1 = "Hola"; // ✓ Dinámico
const char* s2 = "Hola"; // Para constantes

// Comparación
if (s1 == "Hola") { ... } // ✓ String comparison
if (s1 < s2) { ... } // ✓ Ordenamiento lexicográfico

Referencias y Recursos

Documentación

Herramientas

Tutoriales Relacionados

  • Listas enlazadas
  • Estructuras de datos en C++
  • Algoritmos de ordenamiento

Última actualización: Diciembre 2025
Versión: 1.0
Nivel: Técnico/Desarrolladores

avatar
Mynor Cifuentes
Mynor's personal blog
Follow Me
Announcement
Todo el código fuente compartido en este blog se encuentra bajo la licencia MIT. Puedes usar, modificar y distribuir el código para cualquier propósito, siempre y cuando incluyas la nota de copyright y la licencia original.