Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@
cmake-build-debug/
.idea/
build/

# Excalidraw files
*.excalidraw
59 changes: 59 additions & 0 deletions include/avltree.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <iostream>

/**
* El nodo representa un puesto de avanzada. Los puestos de avanzada existen en el mundo postapocaliptico del Refugio 33, y se
* encuentran distribuidos a lo largo del mapa.
*
* outpostId: Es el identificador único del puesto de avanzada.
* prority: Cada puesto de avanzada tiene una prioridad estratégica basada en diversos factores.
*/
struct Nodo {
int outpostId;
int priority;
Nodo* izq;
Nodo* der;

Nodo(int id) : outpostId(id), priority(1), izq(nullptr), der(nullptr) {}
};

class AVLTree {
public:
AVLTree();
~AVLTree();

void insert(int outpostId);
bool contains(int outpostId);
void remove(int outpostId);

private:
Nodo* raiz; // Cuando se instancia, raiz = nullptr (declarado en el constructor)

/**
* @brief Este método se encarga de insertar un nuevo puesto de avanzada (nodo) al Arbol AVL, estructura donde
* se hace el control de los puestos de avanzada.
*
* @param nodo Para insertar un nuevo nodo al arbol se le debe pasar cual será su nodo padre. Normalmente se le pasará
* la raíz como este parámetro, aunque si se está usando en un caso de recursión nodo puedo ser algun sub-hijo.
*
* @param outpostId Es el identificador único del puesto de avanzada que se va a insertar al Arbol AVL.
*
* */
Nodo* insert(Nodo*& nodo, int outpostId);
void liberar(Nodo* nodo);
bool contains(Nodo* raiz, int outpostId);
void remove(Nodo* raiz, int outpostId);

// Métodos auxiliares
int max(int n1, int n2) { return n1 > n2 ? n1 : n2; }
void actualizarAltura(Nodo* nodo);
int priority(Nodo* nodo);
int balance(Nodo* nodo);
Nodo* rotarDerecha(Nodo* nodo);
Nodo* rotarIzquierda(Nodo* nodo);


};




32 changes: 32 additions & 0 deletions include/decisionTree.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include <iostream>

struct Nodo {
std::string decision;
Nodo* izquierda;
Nodo* derecha;

Nodo(const std::string& d)
: decision(d), izquierda(nullptr), derecha(nullptr) {}
};

class DecisionTree {
public:
DecisionTree();
~DecisionTree();

void insertar(const std::string& decision);
bool buscar(const std::string& decision) const;
void eliminar(const std::string& decision);
bool estaVacio() const;
void recorrerPreorden() const;

private:
Nodo* raiz;

void insertar(Nodo*& nodo, const std::string& decision);
bool buscar(Nodo* nodo, const std::string& decision) const;
void eliminarNodo(Nodo*& nodo, const std::string& decision);
void recorrerPreorden(Nodo* nodo) const;
void destruir(Nodo* nodo);
Nodo* encontrarMinimo(Nodo* nodo);
};
190 changes: 190 additions & 0 deletions src/avltree.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#include "avltree.hpp"

// Constructor y destructor
AVLTree::AVLTree() {
raiz = nullptr;
}

AVLTree::~AVLTree() {
liberar(raiz);
}

// Para implementar el destructor primero se debe implementar un metodo que elimine Nodos del Arbol AVL


// Métodos públicos
void AVLTree::insert(int outpostId) {
// Llamamos al método privado, se le pasa la raiz.
raiz = insert(raiz, outpostId);
}

bool AVLTree::contains(int outpostId) {
return contains(raiz, outpostId);
}

void AVLTree::remove(int outpostId) {
if(!contains(raiz, outpostId)) {
std::cout << "El puesto de avanzada con id " << outpostId << " no se encuetra registrado." << std::endl;
} else {
remove(raiz, outpostId);
}
}


// Métodos privados
void AVLTree::remove(Nodo* raiz, int outpostId) {
// El chequeo de si la raiz es null ya se hace en contains, por lo tanto no se vendrá a este método sin verificar que el
// puesto de avanzada existen en el arbol.

if(raiz->outpostId < outpostId) {
remove(raiz->izq, outpostId);
} else if(raiz->outpostId > outpostId) {
remove(raiz->der, outpostId);
} else {
// El sucesor, o nueva raíz o padre, será el más chico del sub arbol derecho


}
}
bool AVLTree::contains(Nodo* raiz, int outpostId) {
if(!raiz) return false;

if(raiz->outpostId == outpostId) {
return true;
}

return contains(raiz->izq, outpostId) || contains(raiz->der, outpostId);
}

void AVLTree::liberar(Nodo* nodo) {
if(!nodo) return;

liberar(nodo->izq);
liberar(nodo->der);
delete nodo;
}
Nodo* AVLTree::insert(Nodo*& nodo, int outpostId) {
// Este if se ejecutará en el caso de que sea el primer nodo a insertar (raiz == null), o en algún llamado de recursión
// cuando se llega a la posición de un nodo hijo que sea null y que en el se pueda insertar el nuevo nodo.
if(!nodo) {
return new Nodo(outpostId);
}

// Sino, se evalúa el valor del ID para insertarlo en el sub-arbol izq. o der.
if(outpostId < nodo->outpostId) {
// Si es menor, se agrega a la izquierda, en este caso, el parámetro nodo es el puntero al nodo izquierdo de la raíz
nodo->izq = insert(nodo->izq, outpostId);
} else if(outpostId > nodo->outpostId) {
// Si es mayor, se agrega a la derecha, en este caso, el parámetro nodo es el puntero al nodo der. de la raíz
nodo->der = insert(nodo->der, outpostId);
} else {
// Sino es ni mayor ni meno quiere decir que es igual, pero no puede haber identificadores
// repetidos, entonces no se agrega. Se retorna el mismo valor de nodo que vino por parametro, dando a entender
// que si era la raiz, esta permanece igual, o si era un nodo padre de algun sub arbol, el nodo padre tampoco
// cambiara
return nodo;
}

// Una vez agregado un nuevo nodo al arbol AVL se debe calcular la nueva altura de este, y si esta fuera del
// rango [-1, 1] se debe balancear el arbol...
// La altura en este arbol es representada por la priority
actualizarAltura(nodo);

// Ahora verificaremos si se encuentra en el rango [-1, 1], y si está fuera se debe balancear

int balanceDelNodo = balance(nodo);

// Desbalanceo Left-Left: Hay un desbalance hacia la izquierda "doble".
if (balanceDelNodo > 1 && outpostId < nodo->izq->outpostId) {
// Retorna la nueva raiz que queda al hacer la rotacion
return rotarDerecha(nodo);
}

// Desbalanceo Right-Right: Hay un desbalance hacia la derecha "doble".
if(balanceDelNodo < -1 && outpostId > nodo->der->outpostId) {
return rotarIzquierda(nodo);
}

// Desbalanceo Left-Right: La raiz esta desbalanceada a la izquierda, y el sub arbol izq esta desbalanceado a la derecha
if(balanceDelNodo > 1 && outpostId > nodo->izq->outpostId) {
nodo->izq = rotarIzquierda(nodo->izq);
return rotarDerecha(nodo);
}

// Desbalanceo Right-Left: La raíz está desbalanceada a la derecha, y el sub arbol der está desbalanceado a la izquierda
if(balanceDelNodo < -1 && outpostId < nodo->der->outpostId) {
nodo->der = rotarDerecha(nodo->der);
return rotarIzquierda(nodo);
}

// Si el balance esta dentro de [-1, 1] no hay que hacer ninguna rotacion, se devuelve el nodo que llego como parametro
// que en ocasiones sera la raiz o un nodo padre en algun caso de recursion
return nodo;
}


// Métodos auxiliares
Nodo* AVLTree::rotarDerecha(Nodo* nodo) {
// Para razonar y comprender el metodo supongamos que el nodo es la raiz, es el caso de una raiz con un sub-arbol izquierdo
// y se agrego otro dato menor que el sub-arbol izquierdo, por ejemplo, raiz 50, sub nodo 40 y se agrego el 20

// El nodo que llega como parametro tiene que convertirse en el sub nodo der de su sub nodo izq
// Y el sub nodo izquierdo del nodo que llego va a ser la nueva raiz o nodo padre

Nodo* nuevoPR = nodo->izq; // nuevoPR = nuevoPadreRaiz para indicar que es el nuevo padre o raiz

// El sub nodo derecho, del sub nodo izquierdo del nodo que llego como parametro (es decir, el sub nodo derecho
// de la nueva raiz o nodo padre) va a ser el sub nodo izquierdo del nodo que llego, que el nodo que llego ahora es
// el sub arbol derecho de su nodo izquierdo (que ahora es el nuevo nodo padre o raiz)
Nodo* nuevoIzqDer = nuevoPR->der; // nuevoIzqDer = nuevo nodo izquierdo del nuevo nodo derecho (que es el nodo que llego como parametro)

// A la derecha de la nueva raiz va el nodo que llego...
nuevoPR->der = nodo;

// ... pero hay que quitarle los nodos hijos al que llego, pq sino estariamos como copiando una rama del arbol...
nodo->izq = nuevoIzqDer; // A la izquierda se quitan el 40 y 20 (en este ejemplo) y se agrega lo que estaba a la derecha
// del nuevo nodo padre o raiz

actualizarAltura(nodo);
actualizarAltura(nuevoPR);

return nuevoPR;

}

Nodo* AVLTree::rotarIzquierda(Nodo* nodo) {
Nodo* nuevoPR = nodo->der;
Nodo* nuevoDerIzq = nuevoPR->izq;

nuevoPR->izq = nodo;
nodo->der = nuevoDerIzq;

actualizarAltura(nodo);
actualizarAltura(nuevoPR);

return nuevoPR;
}

void AVLTree::actualizarAltura(Nodo* nodo) {
if(!nodo) return;

nodo->priority = 1 + max(priority(nodo->izq), priority(nodo->der));

// Siempre que se agregue un nodo, este va a tener altura 1. Entonces el valor de altura que se actuliza es el de
// la raiz o algún sub-nodo si estamos en un caso de recursión.
// Por ejemplo luego de agregar un segundo elemento (ya tenemos la raíz con priority 1), la altura (o priority) de
// la raiz va a ser 1 + la altura del nodo recien agregado que tambien es 1, por lo tanto la raiz queda con una priority
// (o altura) de 2. Y el nodo recién agregado queda con altura 1.
}

int AVLTree::priority(Nodo* nodo) {
if(!nodo) return 0;

return nodo->priority;
}

int AVLTree::balance(Nodo* nodo) {
if(!nodo) return 0;

return priority(nodo->izq) - priority(nodo->der);
}
Loading