Árbol izquierdista / Montón izquierdista

Un árbol de izquierda o un montón de izquierda es una cola de prioridad implementada con una variante de un montón binario. Cada Node tiene un valor s (o rango o distancia) que es la distancia a la hoja más cercana. A diferencia de un montón binario (que siempre es un árbol binario completo ), un árbol de izquierda puede estar muy desequilibrado. A continuación se muestran las complejidades temporales de Leftist Tree/Heap .

  Function       Complexity              Comparison
1) Get Min:       O(1)      [same as both Binary and Binomial]
2) Delete Min:    O(Log n)  [same as both Binary and Binomial]
3) Insert:        O(Log n)  [O(Log n) in Binary and O(1) in 
                            Binomial and O(Log n) for worst case]                                                                  
4) Merge:         O(Log n)  [O(Log n) in Binomial]

Un árbol izquierdista es un árbol binario con propiedades:

  1. Propiedad de almacenamiento dinámico mínimo normal: clave (i)> = clave (padre (i))
  2. Más pesado en el lado izquierdo: dist(right(i)) <= dist(left(i)). Aquí, dist(i) es el número de aristas en la ruta más corta desde el Node i hasta un Node hoja en una representación de árbol binario extendida (en esta representación, un hijo nulo se considera un Node externo o hoja). El camino más corto a un Node externo descendiente es a través del hijo derecho. Cada subárbol también es un árbol de izquierda y dist( i ) = 1 + dist( right( i ) ).

Ejemplo: El siguiente árbol de la izquierda se presenta con su distancia calculada para cada Node con el procedimiento mencionado anteriormente. El Node más a la derecha tiene un rango de 0 ya que el subárbol derecho de este Node es nulo y su padre tiene una distancia de 1 por dist( i ) = 1 + dist( right( i )). Se sigue lo mismo para cada Node y se calcula su valor s (o rango). 

lt1

 De la segunda propiedad anterior, podemos sacar dos conclusiones:

  1. El camino desde la raíz hasta la hoja más a la derecha es el camino más corto desde la raíz hasta una hoja.
  2. Si la ruta a la hoja más a la derecha tiene x Nodes, entonces el montón de la izquierda tiene al menos 2 x – 1 Nodes. Esto significa que la longitud de la ruta a la hoja más a la derecha es O (log n) para un montón a la izquierda con n Nodes.

operaciones: 

  1. La operación principal es merge().
  2. deleteMin() (o extractMin() se puede hacer eliminando la raíz y llamando a merge() para los subárboles izquierdo y derecho.
  3. insert() se puede hacer creando un árbol de izquierda con una sola clave (clave que se insertará) y llamando a merge() para un árbol dado y un árbol con un solo Node.

Idea detrás de la fusión: dado que el subárbol derecho es más pequeño, la idea es fusionar el subárbol derecho de un árbol con otro árbol. A continuación se muestran los pasos abstractos.

  1. Ponga la raíz con menor valor como la nueva raíz.
  2. Cuelgue su subárbol izquierdo a la izquierda.
  3. Combinar recursivamente su subárbol derecho y el otro árbol.
  4. Antes de regresar de la recursividad: – Actualice dist() de la raíz fusionada. – Intercambie los subárboles izquierdo y derecho justo debajo de la raíz, si es necesario, para mantener la propiedad izquierdista del resultado combinado

Pasos detallados para fusionar:

  1. Compara las raíces de dos montones.
  2. Empuje la clave más pequeña en una pila vacía y muévase al hijo derecho de la clave más pequeña.
  3. Compare recursivamente dos claves y continúe empujando la clave más pequeña en la pila y muévase a su hijo derecho.
  4. Repita hasta llegar a un Node nulo.
  5. Tome el último Node procesado y conviértalo en el hijo derecho del Node en la parte superior de la pila, y conviértalo en un montón izquierdo si se violan las propiedades del montón izquierdo.
  6. De forma recursiva, continúe extrayendo los elementos de la pila y convirtiéndolos en el elemento secundario correcto de la nueva parte superior de la pila.

Ejemplo: Considere dos montones izquierdistas que se dan a continuación: 

2

 Fusionarlos en un solo montón izquierdista 

3

 El subárbol en el Node 7 viola la propiedad del montón izquierdo, por lo que lo intercambiamos con el hijo izquierdo y retenemos la propiedad del montón izquierdo. 

4

 Convertir a montón de izquierda. Repita el proceso 

5

6

 La complejidad temporal del peor de los casos de este algoritmo es O(log n) en el peor de los casos, donde n es el número de Nodes en el montón de la izquierda. Otro ejemplo de la fusión de dos montones izquierdistas: 

lt9

Implementación del árbol izquierdista / Montón izquierdista: 

CPP

//C++ program for leftist heap / leftist tree
#include <bits/stdc++.h>
using namespace std;
 
// Node Class Declaration
class LeftistNode
{
public:
    int element;
    LeftistNode *left;
    LeftistNode *right;
    int dist;
    LeftistNode(int & element, LeftistNode *lt = NULL,
                LeftistNode *rt = NULL, int np = 0)
    {
        this->element = element;
        right = rt;
        left = lt,
        dist = np;
    }
};
 
//Class Declaration
class LeftistHeap
{
public:
    LeftistHeap();
    LeftistHeap(LeftistHeap &rhs);
    ~LeftistHeap();
    bool isEmpty();
    bool isFull();
    int &findMin();
    void Insert(int &x);
    void deleteMin();
    void deleteMin(int &minItem);
    void makeEmpty();
    void Merge(LeftistHeap &rhs);
    LeftistHeap & operator =(LeftistHeap &rhs);
private:
    LeftistNode *root;
    LeftistNode *Merge(LeftistNode *h1,
                    LeftistNode *h2);
    LeftistNode *Merge1(LeftistNode *h1,
                        LeftistNode *h2);
    void swapChildren(LeftistNode * t);
    void reclaimMemory(LeftistNode * t);
    LeftistNode *clone(LeftistNode *t);
};
 
// Construct the leftist heap
LeftistHeap::LeftistHeap()
{
    root = NULL;
}
 
// Copy constructor.
LeftistHeap::LeftistHeap(LeftistHeap &rhs)
{
    root = NULL;
    *this = rhs;
}
 
// Destruct the leftist heap
LeftistHeap::~LeftistHeap()
{
    makeEmpty( );
}
 
/* Merge rhs into the priority queue.
rhs becomes empty. rhs must be different
from this.*/
void LeftistHeap::Merge(LeftistHeap &rhs)
{
    if (this == &rhs)
        return;
    root = Merge(root, rhs.root);
    rhs.root = NULL;
}
 
/* Internal method to merge two roots.
Deals with deviant cases and calls recursive Merge1.*/
LeftistNode *LeftistHeap::Merge(LeftistNode * h1,
                                LeftistNode * h2)
{
    if (h1 == NULL)
        return h2;
    if (h2 == NULL)
        return h1;
    if (h1->element < h2->element)
        return Merge1(h1, h2);
    else
        return Merge1(h2, h1);
}
 
/* Internal method to merge two roots.
Assumes trees are not empty, and h1's root contains
smallest item.*/
LeftistNode *LeftistHeap::Merge1(LeftistNode * h1,
                                LeftistNode * h2)
{
    if (h1->left == NULL)
        h1->left = h2;
    else
    {
        h1->right = Merge(h1->right, h2);
        if (h1->left->dist < h1->right->dist)
            swapChildren(h1);
        h1->dist = h1->right->dist + 1;
    }
    return h1;
}
 
// Swaps t's two children.
void LeftistHeap::swapChildren(LeftistNode * t)
{
    LeftistNode *tmp = t->left;
    t->left = t->right;
    t->right = tmp;
}
 
/* Insert item x into the priority queue, maintaining
heap order.*/
void LeftistHeap::Insert(int &x)
{
    root = Merge(new LeftistNode(x), root);
}
 
/* Find the smallest item in the priority queue.
Return the smallest item, or throw Underflow if empty.*/
int &LeftistHeap::findMin()
{
    return root->element;
}
 
/* Remove the smallest item from the priority queue.
Throws Underflow if empty.*/
void LeftistHeap::deleteMin()
{
    LeftistNode *oldRoot = root;
    root = Merge(root->left, root->right);
    delete oldRoot;
}
 
/* Remove the smallest item from the priority queue.
Pass back the smallest item, or throw Underflow if empty.*/
void LeftistHeap::deleteMin(int &minItem)
{
    if (isEmpty())
    {
        cout<<"Heap is Empty"<<endl;
        return;
    }
    minItem = findMin();
    deleteMin();
}
 
/* Test if the priority queue is logically empty.
Returns true if empty, false otherwise*/
bool LeftistHeap::isEmpty()
{
    return root == NULL;
}
 
/* Test if the priority queue is logically full.
Returns false in this implementation.*/
bool LeftistHeap::isFull()
{
    return false;
}
 
// Make the priority queue logically empty
void LeftistHeap::makeEmpty()
{
    reclaimMemory(root);
    root = NULL;
}
 
// Deep copy
LeftistHeap &LeftistHeap::operator =(LeftistHeap & rhs)
{
    if (this != &rhs)
    {
        makeEmpty();
        root = clone(rhs.root);
    }
    return *this;
}
 
// Internal method to make the tree empty.
void LeftistHeap::reclaimMemory(LeftistNode * t)
{
    if (t != NULL)
    {
        reclaimMemory(t->left);
        reclaimMemory(t->right);
        delete t;
    }
}
 
// Internal method to clone subtree.
LeftistNode *LeftistHeap::clone(LeftistNode * t)
{
    if (t == NULL)
        return NULL;
    else
        return new LeftistNode(t->element, clone(t->left),
                            clone(t->right), t->dist);
}
 
//Driver program
int main()
{
    LeftistHeap h;
    LeftistHeap h1;
    LeftistHeap h2;
    int x;
    int arr[]= {1, 5, 7, 10, 15};
    int arr1[]= {22, 75};
 
    h.Insert(arr[0]);
    h.Insert(arr[1]);
    h.Insert(arr[2]);
    h.Insert(arr[3]);
    h.Insert(arr[4]);
    h1.Insert(arr1[0]);
    h1.Insert(arr1[1]);
 
    h.deleteMin(x);
    cout<< x <<endl;
 
    h1.deleteMin(x);
    cout<< x <<endl;
 
    h.Merge(h1);
    h2 = h;
 
    h2.deleteMin(x);
    cout<< x << endl;
 
    return 0;
}
Producción

1
22
5

Este artículo es una contribución de Sheena Kohli y Minal Sunil Parchand . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks. 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *