Iván Yesid Castellanos Motivación Una de las operaciones mas importantes en las estructuras de datos consiste en la búsqueda elementos específicos Ejemplo: buscar si un estudiante existe en la base de datos de la cafetería de la universidad para darle el descuento del almuerzo Si nuestra estructura la manejamos como una lista cualquiera tendríamos que recorrerla toda para hacer la búsqueda (inserción O(1), consulta O(N)) Otra opción para manejar esta situación sería mantener una lista ordenada, es decir, insertar los elementos de forma que la lista siempre esté ordenada y por cada consulta realizar una búsqueda binaria (consulta O(log N), inserción O(N)) Si además queremos eliminar elementos en nuestra estructura sea que se implemente con arreglos o con apuntadores tenemos una complejidad de O(N) Árbol de búsqueda binario También hay forma de implementar la estructura anterior utilizando árboles, en este caso se usarán árboles de búsqueda binarios (BST - Binary Search Tree) Un BST es un árbol binario con la propiedad de que para cada nodo todos los elementos del subárbol del hijo izquierdo (del nodo) son menores al elemento en el nodo y todos los elementos del subárbol del hijo derecho (del nodo) son mayores al elemento en el nodo Conjunto ordenado
En un árbol de búsqueda binario estándar todos los elementos insertados
son diferentes, pues de haber varios iguales no tendría sentido la operación principal que es la de búsqueda Esto motiva el uso de arboles de búsqueda binarios para representar conjuntos (pues en un conjunto solo hay una aparición por elemento) Esto se ha de tener en cuenta para considerar que al insertar no se puede insertar un elemento igual a otro que ya esté (igual desde el punto de vista de la comparación) De igual forma sabremos que al consultar un elemento este es único dentro de ese conjunto TDA conjunto ordenado
Instancias: un conjunto de elementos comparables
Operaciones: EsVacio(): retorna true si el conjunto está vacío Buscar(x): busca si algún elemento del conjunto coincide con x y retorna la referencia de ese elemento en la colección o null en caso de que no esté en el conjunto Consultar(x): retorna true si x está en el conjunto, false en otro caso Eliminar(x): retorna true si el elemento x está en el conjunto y además lo elimina de este, en caso de que x no esté en el conjunto retorna false Insertar(x): inserta el elemento x en el conjunto Definición BinarySearchTree Constructor, EsVacio Insertar
Se utilizará la propiedad de orden en el árbol para insertar el elemento en
el lugar que le debería corresponder en el árbol, para esto vamos a realizar un procedimiento recursivo Si estamos en un nodo y el elemento que queremos insertar es menor al elemento en el nodo entonces inserto el elemento en el subárbol del hijo izquierdo del nodo Si estamos en un nodo y el elemento que queremos insertar es mayor al elemento en el nodo entonces inserto el elemento en el subárbol del hijo derecho del nodo Si estamos en un nodo y el elemento que queremos insertar es igual al elemento en el nodo entonces no hacemos nada Complejidad O(h), donde h es la altura del árbol Ejemplo Insertar Insertar 20, 43, 16, 18, 35, 48, 45, 6 (en ese orden) Insert, auxInsert Buscar / Consultar
Para buscar o consultar la existencia de un elemento en el conjunto
utilizamos fuertemente la propiedad del ordenamiento Iniciamos explorando desde la raíz Si el elemento está en el nodo que estemos explorando entonces terminamos, en caso contrario, si es menor al que estamos explorando nos vamos al subárbol izquierdo y si es mayor nos vamos al árbol izquierdo Realizamos esto hasta encontrar el elemento o hasta llegar a alguna hoja sin haberlo encontrado Complejidad O(h), donde h es la altura del árbol Ejemplo Buscar / Consultar Buscar(19) Buscar(48) Search, contains Encontrar Mínimo, Máximo
Para encontrar el elemento mínimo del árbol basta con ir al descendiente
mas izquierdo posible, es decir, voy recorriendo por la izquierda hasta no tener mas hijos izquierdos En caso de que el árbol sea vacío retorna null Nótese que esto también funciona para encontrar el mínimo de cualquier subárbol del árbol, dado que cada subárbol de un BST es un BST La misma idea funciona para encontrar el máximo del árbol, pero haciendo el recorrido a la derecha Complejidad O(h), h es la altura del arbol Ejemplo Encontrar Mínimo FindMin Eliminar
Para eliminar un elemento del árbol consideraremos 3 posibles
casos: El elemento está en una hoja: en este caso es sencillo eliminar pues solamente hacemos que la referencia al nodo se vuelva nula El elemento está en un nodo con un hijo: este caso también es sencillo, pues hacemos que la referencia a este nodo sea la referencia al hijo El elemento está en un nodo con 2 hijos: En este caso reemplazaremos al elemento con el supremo (el mínimo de los mayores) o el infímo (el máximo de los menores) del elemento y eliminamos luego ese nodo Ese nodo o bien será hoja o tendrá un único hijo (la demostración de este hecho queda como tarea del estudiante), por lo que entra en los 2 primeros casos de eliminación
Complejidad O(h), donde h es la altura del árbol
También puede ser de nuestro interés (como desarrolladores) conocer la
altura del árbol, dado que las complejidades está en orden de esta cantidad De igual forma que con el encontrar mínimo o máximo podemos calcular altura de subárboles del árbol original, utilizaremos polimorfismo para esto De forma recursiva podemos calcular la altura de un árbol con el máximo de las alturas de los subárboles de los hijos izquierdo y derecho La altura de un árbol vacío es 0 Complejidad O(N), donde N es la cantidad de elementos en el arbol Height test BinarySearchTree test BinarySearchTree test BinarySearchTree test BinarySearchTree Test BinarySearchTree Test BinarySearchTree Árboles de búsqueda generales
En general la noción de árboles de búsqueda binarios se puede extender
a árboles de búsqueda n-arios, donde los hijos representan un orden lexicográfico de los elementos Para cada nodo cuyos hijos son a1, a2,…, ak entonces todos los elementos del subárbol a1 son menores a todos los elementos del subárbol a2, los cuales son menores a todos los del árbol a3 y así sucesivamente, los elementos del subárbol ak son mayores a los de los demás subárboles Existen varias estructuras especializadas que manejan este tipo de árboles para almacenamiento de datos y realizar búsquedas eficientes Estructuras Avanzadas basadas en árboles de búsqueda
Árbol de sufijos (Suffix
Tree): Es un árbol que contiene todos los sufijos de un texto, tener este árbol puede ayudar a resolver problemas como el de Longest Common Substring de forma eficiente o el Longest Palindrome, de igual forma tiene aplicaciones en bioinformática y en búsqueda de patrones de texto Estructuras Avanzadas basadas en árboles de búsqueda
Árbol de prefijos (Prefix
Tree) / Trie: Es un árbol que contiene varias palabras, pero la búsqueda se da a partir re prefijos, y los nodos se construyen con las letras de las palabras, es muy eficiente para predicción de texto, y para almacenar bases de datos de palabras (como el diccionario de las palabras en español o en inglés) Estructuras Avanzadas basadas en árboles de búsqueda
Radix Tree: Es un Trie
optimizado, en el cual se comprimen nodos con un único hijo y así utilizar menos memoria y decrementar la altura del árbol, funciona igual que el Trie, pero se debe tener cuidado con el manejo de estos nodos ‘comprimidos’ en la implementación, recomendado cuando la memoria sea limitada