3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín MODULO 08 OBJETIVOS Por medio del desarrollo de este módulo el estudiante deberá: Comprender la especificación del tipo de dato árbol y sus variantes (árbol binario, árbol binario de búsqueda, AVL) Reconocer las particularidades de cada una de estas. CONTENIDO 1 1.1 1.2 1.3 Árboles Definición Terminología Especificación general del árbol 2 Clases de árboles (Especificación) 2.1 Árbol binario 2.1.1 Especificación 2.2 Árbol binario de búsqueda 2.2.1 Especificación 2.3 Árbol AVL 2.3.1 Especificación A Referencias B Taller C Laboratorio 02-2003 1 3004597 – Estructura de Datos – Modulo 08 1 Universidad Nacional de Colombia – Sede Medellín ÁRBOLES Los árboles son estructuras de datos con relaciones jerárquicas, o de “uno a muchos” entre sus componentes. Permiten representar de manera natural muchas de las relaciones entre elementos existentes en el universo. Uno de los ejemplos más comunes de árbol es el árbol genealógico (de hecho, gran parte de la terminología usada para describir árboles es tomada de la genealogía) como se ve en la figura 1.1, de la cual puede inferirse que Luis es el padre de Juan, Pedro y Maria, y a su vez Juan es el padre de Mario y José y María es la madre de Mateo. Figura 1.1: Árbol genealógico Luis Camino simple Juan Mario José Elías Pedro Arista Es el Nodo primer nodo del árbol. María Se caracte riza por ser el único Mateo nodo que no tiene predec esores. ta Nivel 1 Nivel 2 Nivel 3 Nivel 4 1.1 DEFINICIÓN En el contexto de las computadoras, se denomina árboles a aquellas estructuras de datos que presentan relaciones jerárquicas entre sus elementos. Su principal característica es que la relación entre los elementos es “de uno a muchos”, es decir no son lineales (de uno a uno). Por ejemplo, en la figura 1.1 puede verse que un integrante cualquiera puede tener muchos (o ningún) hijos. 1.2 TERMINOLOGÍA Nodo: Los elementos de un árbol son elementos estándar, es decir, tipos de datos que contienen un campo clave (identificador único). Un nodo está compuesto por un solo elemento. Al referirnos a un nodo, implícitamente nos referimos a todos los elementos que lo componen. En los gráficos de árboles, los nodos se representan como esferas y en ocasiones como cajas, dentro del nodo se escribe la clave del elemento que contiene. Arista: Son las líneas que conectan a los nodos. Representan relaciones jerárquicas entre los nodos, por lo tanto, un nodo puede tener ninguna, una o varias aristas que salen de él. 02-2003 2 3004597 – Estructura de Datos – Modulo 08 Nodo raíz: Es el primer nodo del árbol. predecesores (ancestros). Universidad Nacional de Colombia – Sede Medellín Se caracteriza por ser el único nodo que no tiene Nodos hojas: Son aquellos nodos que no tienen sucesores (hijos). Camino simple: Es una secuencia de nodos n1, n2, ... ,nk tales que todos los nodos son distintos y hay una arista entre cada par de nodos (n1, n2), (n2, n3), ... (n(k-1), nk). Se dice que este es un camino simple desde el nodo n1 hasta el nodo nk. Longitud de camino: La longitud de un camino es el número de nodos que este contiene. Puede calcularse como el número de aristas recorridas mas uno. (En algunos textos, es el número de aristas). Por ejemplo, el camino entre Luis y Mario, tiene longitud tres (ver figura 1.1). Padre de un nodo: El predecesor de un nodo se conoce como el padre del nodo. Por ejemplo, como se ve en la figura 1.1, María es el padre de Mateo. Hijo de un nodo: Los sucesores de un nodo se conocen como hijos del nodo. Como se ve en la figura 1.1, Mario y José son los hijos de Juan. Antecesor: Sea A algún nodo de un árbol. Si A es el nodo raíz, entonces A no tiene antecesores. De otro modo, el padre de A y todos los antecesores del padre de A son los antecesores de A. También puede decirse que los antecesores de A son aquellos nodos que se encuentran en el único camino simple que va desde A hasta la raíz del árbol. Por ejemplo, en la figura 1.1, los antecesores de José son Juan y Luis. Descendientes: Si D es un nodo hoja, no tiene descendientes. De otro modo, cada hijo de D y todos los descendientes de cada hijo de D son descendientes de D. Por ejemplo, en la figura 1.1, los descendientes de Juan son Mario, José y Elías. Subárbol: Dado algún nodo de un árbol, dicho nodo junto a todos sus descendientes, forman un subconjunto del árbol llamado subárbol. La figura 1.2 muestra un subárbol de la figura 1.1. Figura 1.2: Subárbol Juan Mario José Elías Hermanos: Dos nodos son hermanos si tienen el mismo padre. Por ejemplo en la figura 1.2 los nodos Mario y José son hermanos. Nivel o profundidad: El nivel de un nodo se define como el número de nodos que se encuentran entre él y la raíz (contando al nodo y la raíz). La raíz tiene un nivel de 1. Por ejemplo, el nodo Mateo de la figura 1.1 se encuentra en el nivel 3. 02-2003 3 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín Altura del árbol: Se define la altura de un árbol como el máximo nivel presente en el árbol. La altura del árbol de la figura 1.1 es 4. 2 CLASES DE ÁRBOLES Existen varios tipos de árboles usados frecuentemente en programación, cada uno de ellos tiene propiedades especiales y usos específicos, pero en general todos se ajustan a la definición de árbol vista en la sección anterior. 2.1 ARBOL BINARIO El árbol binario tiene las siguientes características distintivas: - Cada nodo puede tener como máximo 2 subárboles, y por tanto no puede tener más de dos hijos. - Cada subárbol se identifica como el subárbol izquierdo o el subárbol derecho de su padre. - Puede ser vacío. Alternativamente, puede definirse el árbol binario de manera recursiva: Un árbol binario puede ser vacío, o puede ser un nodo llamado nodo raíz que posee dos subárboles binarios. Estos subárboles binarios son disjuntos entre sí y son disjuntos del nodo raíz, y se llaman el subárbol izquierdo y el subárbol derecho del nodo raíz respectivamente. Uno de los usos más importantes que se le ha dado a los árboles binarios es la representación de expresiones en compiladores, la figura 2.1 ilustra el árbol binario que representa la expresión aritmética (5+3)*(1-(a++)) Figura 2.1: Árbol binario * Subárbol izquierdo del nodo raíz + 5 2.1.1 02-2003 Especificación Subárbol derecho del nodo raíz - 3 1 ++ a Subárbol izquierdo del nodo ++ 4 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín A continuación se presenta la especificación del árbol binario que incluye una colección de operaciones que se pueden efectuar sobre él. Estas operaciones se describen desde un punto de vista lógico y carecen de cualquier tipo de detalles concernientes a la implementación. Especificación TD árbol binario (basada en Stubbs & Webre pg. 181) - Elementos: Los elementos de un árbol binario son nodos. Cada nodo contiene un elemento estándar identificado de manera única por un campo clave. - Estructura: Tiene una estructura jerárquica. Un árbol binario puede ser vacío. En caso contrario es un nodo, llamado nodo raíz unido a dos árboles binarios disjuntos uno de otro y del nodo raíz. Estos árboles se llaman el subárbol izquierdo y derecho respectivamente. Cada nodo (excepto el nodo raíz) tiene un único padre. Cada nodo puede tener uno o dos hijos, o bien no tener ninguno. Cada hijo se denota como hijo izquierdo o derecho de su padre. - Operaciones: Cada una de las operaciones, excepto crear(), asume la existencia de un árbol binario. Ocasionalmente, en la poscondición, es necesario referirse al árbol o al nodo actual antes de la operación. Se usará la notación T-pre (tree previous) y c-pre (current previous) para estas referencias. recorrer(ord) PRE: El árbol es no vacío POST: Cada nodo en el árbol ha sido procesado exactamente una vez. El orden en que los nodos fueron procesados depende del valor de ord, como se muestra a continuación: Caso de ord: preorden: Cada nodo es procesado antes de su subárbol izquierdo y antes de su subárbol derecho. inorden: Cada nodo es procesado después de todos los nodos en su subárbol izquierdo y antes de cualquier nodo en su subárbol derecho. postorden: Cada nodo es procesado después de su subárbol izquierdo y de su subárbol derecho. insertar(e, rel) PRE: puede darse ya sea que rel=raiz y el árbol es vacío o que rel raiz y el árbol es no vacío. POST: dependiendo del valor de rel, e pudo haber sido añadido. Si e fue añadido, el nodo que contiene a e es el actual. Caso de rel: raíz: e es el elemento en la raíz del árbol. hijoizq: si c-pre no tenía hijo izquierdo entonces tiene un hijo izquierdo que contiene a e, de otro modo la función da error. hijoder: si c-pre no tenía hijo derecho entonces tiene un hijo derecho que contiene a e, de otro modo la función da error. borrarsub() PRE: el árbol es no vacío. POST: el subárbol de T-pre cuya raíz es c-pre ha sido removido del árbol. El nodo raíz es el actual (o nulo si c-pre era la raíz del árbol) . 02-2003 5 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín actualizar(e) PRE: el árbol es no vacío. POST: el elemento del nodo actual tiene el valor de e. e = leer() PRE: el árbol es no vacío. POST: e tiene el valor del elemento del nodo actual. st = características() PRE: el árbol es no vacío. POST: st contiene el tamaño (número de nodos del árbol), la altura y la longitud promedio de un camino desde la raíz hasta un nodo hoja del árbol. ir_a(rel) PRE: el árbol es no vacío. POST: el nodo actual está determinado por el valor de rel como se indica a continuación. caso de rel: raiz: El nodo raíz es el actual. padre: si c-pre tenía un nodo padre, este es el actual. De otro modo da error. hijoizq: si c-pre tenía un hijo izquierdo, este es el actual. De otro modo da error. hijoder: si c-pre tenía un hijo derecho, este es el actual. De otro modo da error. r = vacio() PRE: ninguna. POST: si T-pre es vacío, el valor de r es VERDADERO, de otro modo el valor de r es FALSO. crear() PRE: no existe árbol. POST: existe un árbol vacío. destruir() PRE: el árbol es no vacío. POST: el árbol es vacío 2.2 ÁRBOL BINARIO DE BÚSQUEDA Un árbol binario de búsqueda es un tipo especial de árbol binario en el cual la posición de cada nodo en el árbol está determinada por el valor de alguno de los campos del elemento guardado en el nodo (generalmente el campo clave) a este campo del elemento del nodo se le llamará campo de clasificación. Consideremos la búsqueda de un nodo que contiene un elemento particular. Este nodo se llamará nodo objetivo, la búsqueda se inicia en la raíz del árbol, si la raíz es el nodo objetivo habrá 02-2003 6 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín concluido la búsqueda, pero si no lo es a continuación hay que decidir si la búsqueda continuará por el subárbol izquierdo o por el subárbol derecho de la raíz. El árbol binario de búsqueda permite saber cual de los subárboles debe seguirse para llegar al nodo objetivo. Una consecuencia de esto es que como máximo tendrá que recorrerse un número de nodos igual a la altura del árbol para encontrar el nodo objetivo. El árbol binario de búsqueda, como lo insinúa su nombre, hace que el proceso de buscar un nodo que contenga un elemento en particular sea altamente eficiente. Por supuesto, para asegurar el correcto funcionamiento del árbol binario de búsqueda es necesario restringir las funciones de inserción y actualización para garantizar su integridad. Un árbol binario de búsqueda es un árbol binario tal que para cada nodo N, las siguientes afirmaciones son verdaderas: - Si L es algún nodo en el subárbol izquierdo de N, entonces el campo de clasificación de L es menor que el campo de clasificación de N. - Si R es algún nodo en el subárbol derecho de N, entonces el campo de clasificación de R es mayor que el campo de clasificación de N. Ejemplo 2.1: Búsqueda en un árbol binario de búsqueda En la figura 2.1 se muestra un árbol binario de búsqueda. A continuación se describe paso a paso el proceso de búsqueda del nodo cuyo elemento tiene un valor de 10. Primero se busca en la raíz, cuyo valor es 5. Como 10 es mayor que 5 se procede a buscar en el subárbol derecho de la raíz. Ahora estamos en el nodo 8, como 10 es mayor que 8, se procede a buscar en el subárbol derecho de 8. Ahora hemos llegado al nodo 12, como 10 es menor que 12, se procede a buscar en el subárbol izquierdo del nodo 12. Se ha encontrado el nodo con elemento 10. Nótese que en el peor de los casos (en el que el elemento buscado no está en el árbol) se habría tenido que recorrer como máximo un número de elementos igual a la altura del árbol (que en el caso de la figura 2.1 es 5), mientras que en una lista se tendrían que recorrer todos los componentes de la lista (por ejemplo, si el árbol de la figura 2.1 estuviera implementado en una lista, al buscar un elemento que no pertenece a la lista se tendrían que recorrer 9 elementos). Este efecto crece dramáticamente cuando el número de elementos manejados es muy grande, los tiempos de búsqueda en una lista comparados con los de un árbol binario de búsqueda serán abismalmente mayores. 02-2003 7 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín Figura 2.1: Árbol binario de búsqueda Búsqueda del elemento 10 5 2 1 0 8 4 12 10 11 2.2.1 Especificación Muchas de las operaciones del árbol binario son exactamente iguales en el árbol binario de búsqueda. actualizar e insertar deben ser modificadas para conservar la estructura del árbol de búsqueda, además se añaden dos funciones nuevas: encontrar_clave y borrar_clave que toman ventaja de las características del árbol de búsqueda. Especificación TD árbol binario de búsqueda (basada en Stubbs & Webre pg. 207) - Elementos: Los elementos de un árbol binario de búsqueda son nodos. Se asume que cada nodo contiene un elemento estándar y es identificado de manera única por el campo clave del elemento. - Estructura: La estructura del árbol binario de búsqueda es la misma del árbol binario excepto que en el árbol de búsqueda, si N es algún nodo del árbol todos los nodos en su subárbol izquierdo tienen campos de clasificación menores que los de N y todos los nodos en su subárbol derecho tienen campos de clasificación mayores que los de N. - Operaciones: T-pre se refiere al árbol antes de que fuera ejecutada la operación. Todas las operaciones del árbol binario aplican y son idénticas, excepto las que se listan a continuación. insertar(e) PRE: existe el árbol. POST: si T-pre no contenía el elemento e, entonces e es un elemento del árbol, de otro modo la función genera error. 02-2003 8 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín borrar_clave(key) PRE: existe el árbol y es no-vacío. POST: si T-pre contenía un elemento con campo clave igual a key, entonces ese elemento ya no está en el árbol. De otro modo la función reporta error. Si el árbol es no vacío el nodo raíz es el actual. actualizar(e) PRE: el árbol es no vacío. POST: el elemento del nodo actual es e. El árbol sigue siendo un árbol binario de búsqueda. findkey(key) PRE: el árbol es no vacío. POST: si el árbol contiene un nodo cuyo campo clave tiene el mismo valor de key, entonces ese nodo es el actual. De otro modo el nodo actual es el nodo (hoja) al cual se conectaría como hijo un nodo con campo clave key de ser insertado. Las nuevas operaciones del árbol binario de búsqueda son más poderosas y más restrictivas. La operación findkey se beneficia de la estructura del árbol binario de búsqueda, que la hace muy eficiente, como se vio anteriormente. La operación actualizar es particularmente importante, ya que es su deber proteger la integridad del árbol binario de búsqueda. Si el usuario quiere cambiar el elemento de un nodo, entonces el campo clave del nuevo elemento debe ser mayor que todos los campos clave del subárbol izquierdo y menor que todos los campos clave del subárbol derecho. Si este no es el caso, el árbol debe ser reestructurado para acomodar el nuevo elemento. Una forma sencilla de hacer esto es borrar el elemento a ser modificado y luego insertar un elemento con los nuevos valores. Dado un elemento, la operación insertar determina la posición apropiada y añade al árbol un nodo que contiene el elemento a insertar. Nótese que la posición de inserción no puede ser especificada por el usuario, quien podría cometer un error y arruinar la integridad del árbol. En módulos posteriores se verá en detalle el funcionamiento de todas estas funciones y como aplicarlas para crear un gestor de registros. 2.3 ÁRBOL AVL Un árbol AVL es un caso especial de un árbol binario con altura balanceada. Un árbol binario es un árbol-p de altura balanceada si para cada nodo en el árbol, la diferencia de las alturas de sus dos subárboles es máximo p. Un árbol AVL es un árbol-1 de altura balanceada, es decir, en un árbol AVL la diferencia de las alturas de los dos subárboles de cualquier nodo no puede ser mayor que 1. 02-2003 9 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín Figura 2.2: Árbol AVL 4 2 1 8 3 6 5 9 7 10 Puede demostrarse que la altura de un árbol AVL jamás excede 1.45Log 2n. Estudios empíricos demuestran que la altura es aproximadamente log2n donde n es el número de nodos del árbol, esto asegura que el número máximo de elementos que tendrían que recorrerse en una búsqueda (este número es la altura del árbol) nunca será mayor a 1.45Log 2n, mientras que en un árbol binario de búsqueda normal podría llegar a ser un número muy cercano a n (n si el árbol está "degenerado", es decir sesgado hacia la derecha o hacia la izquierda, ver figura 2.3). Figura 2.3: árbol binario de búsqueda no AVL degenerado 1 4 7 9 2.3.1 Especificación Todas las operaciones del árbol binario de búsqueda excepto insertar y borrar también se aplican al árbol AVL. Los árboles AVL considerados en esta sección están construidos sobre el árbol binario de búsqueda. En módulos posteriores se discutirá en detalle la función insertar. 02-2003 10 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín Especificación TD árbol AVL (basada en Stubbs & Webre pg. 226) Elementos: Los elementos de un árbol AVL son nodos. Se asume que cada nodo contiene un elemento estándar y es identificado de manera única por el campo clave del elemento. Estructura: Los nodos se estructuran para formar un árbol binario de búsqueda de manera que la diferencia en la altura de dos subárboles de cualquier nodo será como máximo 1. Operaciones: Todas las operaciones del árbol binario de búsqueda, excepto insertar y borrar aplican al árbol AVL. En este módulo sólo se considerará la función insertar. insertar(e) PRE: Existe el árbol AVL y no contiene un elemento cuyo valor es e. POST: El árbol contiene a e y sigue siendo AVL. 02-2003 11 3004597 – Estructura de Datos – Modulo 08 Universidad Nacional de Colombia – Sede Medellín ANEXOS A REFERENCIAS [DS1984] Stubbs. Daniel F. & Webre, Neil W., “Data Structures: with Abstract Data Types and Pascal”, Brooks/Cole Publishing Company, 1984. B TALLER 1 Señale las diferencias entre una estructura de datos lineal (como las listas) y una jerárquica (árboles.) 2 Considere el árbol de la figura B.1. a. Cuál es su altura? b. Cuales son sus nodos hoja? c. Cuál es el único camino simple del nodo raíz a cada uno de los nodos hoja? 3 Muestre como puede usarse un árbol binario para representar las relaciones entre una persona y todos los ancestros de esa persona. Dibuje el árbol con sus propios ancestros. Que persona está en la raíz del árbol? 4 Dibuje un árbol AVL insertando cada uno de los siguientes nodos: 5, 7, 6, 9, 2, 3, 4 recuerde que debe mantener la estructura del árbol binario de búsqueda y equilibrar el árbol. Figura B.1: Ejercicio 2 A D E F K Q J O Z C CREDITOS Editor: PhD. Fernando Arango Colaboradores: Ing. Edwin Hincapié, Ms.C. Francisco Moreno, Santiago Londoño, Alberto Jiménez, Juan Carlos Hernández, Carlos Andrés Giraldo, Diego Figueroa. 02-2003 12