Implementación de Árbol Binario AVL en C++: Clases y Métodos

Clasificado en Informática

Escrito el en español con un tamaño de 7,94 KB

Árbol Binario AVL: Definición de Clases

Clase Nodo (Nodo)


template<class DATO>
class AVL;

// Clase Nodo de Árbol AVL:
template<class DATO>
class Nodo {
public:
    // Constructor:
    Nodo(const DATO dat, Nodo<DATO> *pad = NULL,
         Nodo<DATO> *izq = NULL, Nodo<DATO> *der = NULL) :
        dato(dat), padre(pad), izquierdo(izq), derecho(der), FE(0) {}

    // Miembros:
    DATO dato;
    int FE; // Factor de Equilibrio
    Nodo<DATO> *izquierdo;
    Nodo<DATO> *derecho;
    Nodo<DATO> *padre;

    friend class AVL<DATO>;
};

Clase Árbol AVL (AVL)


template<class DATO>
class AVL {
private:
    enum { IZQUIERDO, DERECHO };

    // Punteros del árbol:
    Nodo<DATO> *raiz;
    Nodo<DATO> *actual;
    int contador;
    int altura;

public:
    // Constructor y destructor básicos:
    AVL() : raiz(NULL), actual(NULL), contador(0), altura(0) {}
    ~AVL() { Podar(raiz); }

    // --- Operaciones Principales ---
    // Insertar en árbol ordenado:
    void Insertar(const DATO dat);
    // Borrar un elemento del árbol:
    void Borrar(const DATO dat);
    // Función de búsqueda:
    bool Buscar(const DATO dat);

    // --- Comprobaciones --- 
    // Comprobar si el árbol está vacío:
    bool Vacio(Nodo<DATO> *r) { return r == NULL; }
    // Comprobar si es un nodo hoja:
    bool EsHoja(Nodo<DATO> *r) { return !r->derecho && !r->izquierdo; }

    // --- Métodos de Acceso e Información ---
    // Contar número de nodos:
    const int NumeroNodos();
    const int AlturaArbol();
    // Calcular altura de un dato:
    int Altura(const DATO dat);
    // Devolver referencia al dato del nodo actual:
    DATO &ValorActual() { return actual->dato; }
    // Moverse al nodo raíz:
    void Raiz() { actual = raiz; }

    // --- Recorridos --- 
    // Aplicar una función a cada elemento del árbol:
    void InOrden(void (*func)(DATO&, int), Nodo<DATO> *nodo = NULL, bool r = true);
    void PreOrden(void (*func)(DATO&, int), Nodo<DATO> *nodo = NULL, bool r = true);
    void PostOrden(void (*func)(DATO&, int), Nodo<DATO> *nodo = NULL, bool r = true);

private:
    // --- Funciones de Equilibrado --- 
    void Equilibrar(Nodo<DATO> *nodo, int rama, bool nuevo);
    void RSI(Nodo<DATO>* nodo); // Rotación Simple Izquierda
    void RSD(Nodo<DATO>* nodo); // Rotación Simple Derecha
    void RDI(Nodo<DATO>* nodo); // Rotación Doble Izquierda-Derecha
    void RDD(Nodo<DATO>* nodo); // Rotación Doble Derecha-Izquierda

    // --- Funciones Auxiliares ---
    void Podar(Nodo<DATO>* &nodo); // Elimina todos los nodos
    void auxContador(Nodo<DATO>* nodo);
    void auxAltura(Nodo<DATO>* nodo, int a);
};

Árbol Binario AVL: Implementación de Métodos

Método Podar

Elimina todos los nodos del subárbol a partir del nodo proporcionado (incluido él), usando un recorrido en postorden.


template<class DATO>
void AVL<DATO>::Podar(Nodo<DATO>* &nodo)
{
    // Algoritmo recursivo, recorrido en postorden
    if (nodo) {
        Podar(nodo->izquierdo); // Podar subárbol izquierdo
        Podar(nodo->derecho);   // Podar subárbol derecho
        delete nodo;            // Eliminar nodo actual
        nodo = NULL;
    }
}

Método Insertar

Inserta un dato en el árbol AVL, manteniendo la propiedad de árbol binario de búsqueda y realizando el equilibrado si es necesario.


template<class DATO>
void AVL<DATO>::Insertar(const DATO dat)
{
    Nodo<DATO> *padre = NULL;

    // std::cout << "Insertar: " << dat << std::endl; // Descomentar para depuración

    actual = raiz;
    // Buscar la posición de inserción
    while (!Vacio(actual) && dat != actual->dato) {
        padre = actual;
        if (dat > actual->dato) {
            actual = actual->derecho;
        } else if (dat < actual->dato) {
            actual = actual->izquierdo;
        }
    }

    // Si el dato ya existe, no se inserta
    if (!Vacio(actual)) return;

    // Insertar el nuevo nodo
    if (Vacio(padre)) { // El árbol estaba vacío
        raiz = new Nodo<DATO>(dat);
    } else if (dat < padre->dato) {
        padre->izquierdo = new Nodo<DATO>(dat, padre);
        Equilibrar(padre, IZQUIERDO, true);
    } else if (dat > padre->dato) {
        padre->derecho = new Nodo<DATO>(dat, padre);
        Equilibrar(padre, DERECHO, true);
    }
}

Método Equilibrar

Revisa y ajusta los factores de equilibrio (FE) hacia la raíz desde el nodo insertado/borrado, aplicando las rotaciones necesarias (RSI, RSD, RDI, RDD) para mantener el árbol AVL balanceado.


template<class DATO>
void AVL<DATO>::Equilibrar(Nodo<DATO> *nodo, int rama, bool nuevo)
{
    bool salir = false;

    // Recorrer el camino hacia la raíz desde el padre del nodo insertado/borrado
    while (nodo && !salir) {
        // Ajustar FE según la operación (inserción o borrado)
        if (nuevo) { // Inserción
            if (rama == IZQUIERDO) nodo->FE--;
            else nodo->FE++;
        } else { // Borrado
            if (rama == IZQUIERDO) nodo->FE++;
            else nodo->FE--;
        }

        // Evaluar el FE del nodo actual
        if (nodo->FE == 0) { // El árbol se ha equilibrado en este punto
            salir = true;
        } else if (nodo->FE == -2) { // Desequilibrio hacia la izquierda
            if (nodo->izquierdo->FE == 1) RDD(nodo); // Rotación Doble Derecha-Izquierda
            else RSD(nodo); // Rotación Simple Derecha
            salir = true; // Después de una rotación, el subárbol queda equilibrado
        } else if (nodo->FE == 2) { // Desequilibrio hacia la derecha
            if (nodo->derecho->FE == -1) RDI(nodo); // Rotación Doble Izquierda-Derecha
            else RSI(nodo); // Rotación Simple Izquierda
            salir = true; // Después de una rotación, el subárbol queda equilibrado
        }

        // Subir al padre para continuar el proceso si no se ha salido
        if (nodo->padre) {
            // Determinar por qué rama se sube (izquierda o derecha del padre)
            if (nodo->padre->derecho == nodo) rama = DERECHO;
            else rama = IZQUIERDO;
        }
        nodo = nodo->padre; // Moverse al siguiente nodo en el camino hacia la raíz
    }
}

Entradas relacionadas: