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:
- Propiedad de almacenamiento dinámico mínimo normal: clave (i)> = clave (padre (i))
- 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).
De la segunda propiedad anterior, podemos sacar dos conclusiones:
- 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.
- 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:
- La operación principal es merge().
- deleteMin() (o extractMin() se puede hacer eliminando la raíz y llamando a merge() para los subárboles izquierdo y derecho.
- 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.
- Ponga la raíz con menor valor como la nueva raíz.
- Cuelgue su subárbol izquierdo a la izquierda.
- Combinar recursivamente su subárbol derecho y el otro árbol.
- 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:
- Compara las raíces de dos montones.
- 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.
- Compare recursivamente dos claves y continúe empujando la clave más pequeña en la pila y muévase a su hijo derecho.
- Repita hasta llegar a un Node nulo.
- 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.
- 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:
Fusionarlos en un solo montón izquierdista
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.
Convertir a montón de izquierda. Repita el proceso
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:
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; }
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