Ministerio del poder popular para la defensa Universidad nacional experimental De la fuerza armada UNEFA-NIRGUA
ESTRUCTURAS DINAMICAS DE INFORMACION ARBOLES
Ing. Luis Sequera.
Integrantes. Ortega Mariannis. 20.081.457
Ing. De sistemas. 7mo semestre. Febrero del 2014
Árbol: Estructura no lineal y dinámica de datos. Dinámica: puede cambiar durante la ejecución de un programa.
No lineal: a cada elemento del árbol pueden seguirle varios elementos. Están formados por un conjunto de nodos y un conjunto de aristas que conectan pares de nodos.
Definición no recursiva. Conjunto de nodos y conjunto de aristas que conectan pares de nodos con las siguientes características: Se distingue un nodo raíz (no tiene padre). • A cada nodo c (excepto la raíz) le llega una arista desde exactamente un nodo p diferente a c, al cual se le llama padre de c. • Hay un único camino desde la raíz hasta cada nodo. La misma longitud del camino es su número de aristas. •
Definición recursiva. Un árbol es o bien vacío o consiste en una raíz y cero o más subárboles no vacíos , ,…, , cada una de cuyas raíces está conectada por medio de una arista con la raíz.
Definiciones •
Los nodos que no tienen hijos se denominan hojas.
Un árbol con N nodos debe tener (N-1) aristas. • La profundidad de la raíz es 0 y la de cualquier nodo es la de su padre más 1. • La altura de un nodo es 1 más que la mayor altura de un hijo suyo. La altura de un árbol es la altura de la raíz. • Los nodos que tienen el mismo padre son hermanos. • Si hay un camino del nodo u al nodo v, u es ascendiente •
de v y v es descendiente de u. Si u v son propios. • El tamaño de un nodo es el número de descendientes (incluido él mismo). El tamaño de un árbol es el tamaño de su raíz.
Operaciones básicas.
Insertar. Buscar. Eliminar. Ir a la raíz. Recorrer.
Arboles binarios Un árbol binario es o bien vacío o consta de una raíz, un hijo árbol binario izquierdo y otro derecho. Los árboles binarios de búsqueda permiten inserciones y acceso a los elementos en tiempo logarítmico. Los árboles binarios llamados colas con prioridad soportan acceso y eliminación del mínimo de una colección de elementos.
Árboles binarios de búsqueda. Para todo nodo A del árbol: • Todos los valores de los nodos del subárbol izquierdo de A deben ser menores al valor del nodo A. Todos los valores de los nodos del subárbol derecho de A deben ser mayores o iguales al valor del nodo A. Un recorrido en in orden del árbol proporciona una lista en orden ascendente de los valores almacenados en los nodos. Para describir las operaciones, se considera que estas se enmarcan dentro de la clase Nodo Binario. El lenguaje utilizado es JAVA. •
Operación “buscar” public boolean buscar(Object o) { if (o.equals(valor)) return true; else if (o.compareTo(valor)<0) return buscar(getIzq(),o); else return buscar(getDer(),o); }
Operación “insertar”
public NodoBinario insertar(Comparable o){ if (o.compareTo(valor)<0) setIzq(insertar(getIzq(),o)); else setDer(insertar(getDer(),o)); return this; } Dentro de la clase NodoBinarioVacio: public NodoBinario insertar(Comparable o) { return new NodoBinario(o); }
Operación “recorrer” Los recorridos pueden ser en preorden, postorden o inorden (orden simétrico). Todos son O(N). public void preOrder(SList aList) { aList.addElement(value); left.preOrder(aList); right.preOrder(aList); } public void inOrder(SList aList) {
left.inOrder(aList); aList.addElement(value); right.inOrder(aList); } public void posOrder(SList aList) { left.posOrder(aList); right.posOrder(aList); aList.addElement(value); } Los recorridos no necesitan obligatoriamente recursividad, se puede emplear una pila para realizarlos iterativamente.
Operación “borrado” El nodo a borrar debe ser reemplazado por el nodo más a la derecha en el subárbol izquierdo o el nodo más a la izquierda en el subárbol derecho (el nodo más a la derecha del subárbol izquierdo será mayor o igual que cualquier otro nodo de ese subárbol y menor que todos los del subárbol derecho, y el nodo más a la izquierda del subárbol derecho será menor que todos los demás nodos de ese subárbol y mayor que todos los del subárbol izquierdo). Para el caso en el que el nodo elegido tengo un subárbol, hay por lo menos tres soluciones posibles:
La primera consiste en conservar la estructura del subárbol, y colgar del elemento ubicado en el extremo (el elemento menor o mayor) correspondiente al subárbol donde se encuentra el elemento a promover hacia la raíz (en este ejemplo, el subárbol izquierdo, por lo cual se buscará el elemento más a la izquierda), lo cual es consistente, porque todos los elementos en el subárbol promovido serán mayores que los del subárbol del cual estaban colgados a la derecha. El inconveniente que presenta esta solución es que debe utilizarse una función encontrarMínimo() o encontrarMáximo(). • La segunda solución consiste en colgar del padre del nodo promovido hacia la raíz, el subárbol remanente. Esto es consistente, porque todo elemento del subárbol derecho de un nodo será mayor que el valor de ese nodo, y viceversa. Estas soluciones aprovechan la ventaja de contar con que el nodo promovido tiene, a lo sumo, un subárbol. • Un hueco dejado por un nodo promovido también puede pensarse como una eliminación. •
Árboles binarios perfectamente equilibrados La eficiencia de las operaciones depende exclusivamente de la altura del árbol. Para un árbol de N nodos
perfectamente equilibrado el coste de acceso es de orden logarítmico: O(log N). Sin embargo, se dice que si el árbol crece o decrece descontroladamente, el rendimiento puede disminuir considerablemente, siendo para el caso más desfavorable (insertar un conjunto de claves ordenadas en forma ascendente o descendente) el coste de acceso: O(N). En un árbol binario perfectamente equilibrado, el número de nodos en el subárbol izquierdo y el número de nodos en el subárbol derecho, difieren como mucho en una unidad, y los subárboles son también equilibrados
Árboles equilibrados Un procedimiento de inserción que siempre restaure la estructura del árbol a un equilibrio perfecto es poco eficiente. Se usa una formulación menos estricta de “equilibrio”: Un árbol está equilibrado si para cada uno de sus nodos ocurre que las alturas de sus dos subárboles difieren como mucho en 1(Árboles AVL).
Factor de equilibrio (FE) de un nodo El FE es la altura del subárbol izquierdo menos la altura del subárbol derecho. Los valores que puede tomar son -1, 0, 1. Si llegara a tomar los valores -2 o 2 debe reestructurarse el árbol. Todos los árboles perfectamente equilibrados son AVL. La longitud de camino media es prácticamente idéntica a la de un árbol perfectamente equilibrado. En un AVL se puede realizar con complejidad del O(log N) las siguientes operaciones: •
Encontrar un nodo con una clave dada.
Insertar un nodo con una clave dada. • Borrar un nodo con una clave dada. Un árbol AVL de altura H tiene por lo menos (Fibonacci(H+3) -1) nodos. Los pasos necesarios para insertar un nodo en un árbol AVL son: • Agregar el nodo como en un árbol binario de búsqueda. • En el regreso por el camino de búsqueda se comprueba el FE de los nodos. • Si un nodo presenta un FE incorrecto (2 o -2) se reestructura el árbol y se continúa el ascenso hasta llegar a la raíz. Casos en situación de reestructurar: 1. Una inserción en el subárbol izquierdo del hijo izquierdo de X. 2. Una inserción en el subárbol derecho del hijo izquierdo de X. 3. Una inserción en el subárbol izquierdo del hijo derecho de X. 4. Una inserción en el subárbol derecho del hijo derecho de X. •
Inserciones en los “márgenes” 1 y 4: inserciones en los “márgenes”: rotación simple: intercambia los papeles de los padres y de los hijos, manteniendo la ordenación.
// Rotación izquierda - izquierda private static NodoAVL rotarConHijoIzq(NodoAVL A) { NodoAVL B = (NodoAVL) A.getIzq(); //Asigna nombre A.setIzq(B.getDer()); B.setDer(A); return B; }
Inserciones por “dentro” 2 y 3: inserciones por “dentro”: rotación doble. Notar que una rotación simple no resuelve el problema, ya que la rama que provocó el desequilibrio se descuelga del nodo promovido y se cuelga al nodo que desciende un nivel, de manera que se mantiene con la misma profundidad, que es la que provocó el desequilibrio. Por lo tanto, antes de rotar, debe desplazarse el desequilibrio a la rama correspondiente, es decir, transformamos el caso de una inserción por dentro, a un caso de inserción en el margen, utilizando una rotación. // Rotación izquierda - derecha private static NodoAVL rotarDobleConHijoIzq(NodoAVL A) { NodoAVL B = (NodoAVL) A.getIzq(); //Asigna nombre A.setIzq(rotarConHijoDer((NodoAVL) B)); return rotarConHijoIzq(A);
} Como se ve, el problema se convirtió en un problema igual al del primer caso. Los pasos necesarios para suprimir un nodo en un árbol AVL son: Suprimir el nodo como en un árbol binario de búsqueda. • En el regreso por el camino de supresión se comprueba el FE de los nodos. • Si un nodo presenta un FE incorrecto (2 o -2) se reestructura el árbol y se continúa el ascenso hasta llegar a la raíz. La reestructuración se efectúa cuando al regresar por el camino de búsqueda después de una inserción o una supresión se comprueba que la condición del FE se ha violado. Una supresión puede provocar varias reestructuraciones. En la práctica se utilizan otros esquemas de equilibrio como los árboles rojinegros: como en los AVL las operaciones son logarítmicas en el peor caso. La ventaja es que las inserciones y eliminaciones pueden realizarse con un único recorrido descendente. •