Cap. 6 - Universidad de La Serena
Transcripción
Cap. 6 - Universidad de La Serena
Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. CAPITULO 6 - Estructuras de Datos Avanzadas Contenidos: 1. Estructura de Datos Avanzadas. 2. SkipList, 3. Trie, Arboles Sufijo, Arboles Búsqueda Digital, Arboles Trie y Patricia, 4. Algoritmos de Selección, Arboles Min –Max, y otros. 5. Enfasis en transformaciones, rotaciones, buscar, insertar y borrar con sus costos asociados y aplicaciones. Skip lists Se sabe que una de las maneras más simples de implementar el TDA diccionario es utilizando una lista enlazada, pero también se sabe que el tiempo de búsqueda promedio es O(n) cuando el diccionario posee n elementos. Es decir, el costo de una búsqueda en el caso peor es lineal en el número de nodos. Por otra parte, claramente es imposible realizar una búsqueda binaria, ya sea por bisección o por trisección, sobre una lista enlazada ordenada; porque no existe manera de calcular desde las direcciones de dos elementos i y j, con valores xi y xj para X, la dirección de un elemento con un valor de X intermedio entre xi y xj . Esto se debe a que las celdas que contienen elementos de la lista pueden provenir arbitrariamente desde cualquier parte de la memoria. De hecho, ésa fue la razón que nos llevó a acotar el uso de dicho tipo de listas; a pesar de que las modificaciones estructurales sobre ellas eran poco costosas (de orden (1)). Es así como la localización es secuencial y, por lo tanto, el peor caso de localización exitosa sería de N celdas consultadas (el peor caso de localización sería de N +1 celdas consultadas). La figura siguiente muestra un ejemplo de lista enlazada simple con “head”, donde los elementos están ordenados ascendentemente: La existencia de las Skip List, se debe William Pugh: De manera que, es posible modificar la estructura de datos de la lista y así mejorar el tiempo de búsqueda promedio. La siguiente figura muestra una lista enlazada en donde a cada nodo par se le ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 1 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. agrega una referencia al nodo ubicado dos posiciones más adelante en la lista. Modificando ligeramente el algoritmo de búsqueda, a lo más techo(n/2)+1 nodos son examinados en el peor caso. Tal como se puede apreciar, para n=10, Esta idea se puede extender agregando una referencia cada cuatro nodos. En este caso, a lo más techo(n/4)+2 nodos son examinados: El caso límite para este argumento se muestra en la siguiente figura. Cada 2i nodo posee una referencia al nodo 2i posiciones más adelante en la lista. El número total de punteros se ha duplicado (con respecto a la lista enlazada inicial). Ahora el tiempo de una búsqueda está acotado superiormente por techo(log2 n), porque la búsqueda consiste en avanzar al siguiente nodo (por el puntero alto) o bajar al nivel siguiente de punteros y seguir avanzando…En esencia, se trata de una búsqueda binaria. Notar que el número total de referencias solo ha sido doblado, pero ahora a lo más techo(log2 n) nodos son examinados durante la búsqueda. Note que la búsqueda en esta estructura de datos es básicamente una búsqueda binaria, por lo que el tiempo de búsqueda en el peor caso es O(log n). El problema que tiene esta estructura de datos es que es demasiado rígida para permitir inserciones de manera eficiente. Por lo tanto, es necesario relajar levemente las condiciones descritas anteriormente para permitir inserciones eficientes. Se define un nodo de nivel k como aquel nodo que posee k referencias. Se observa de la figura anterior que, aproximadamente, la mitad de los nodos son de nivel 1, que un cuarto de los nodos son de nivel 2, etc. En general, aproximadamente n/2i nodos son de nivel i. Cada vez que se inserta un nodo, se elige el nivel que tendrá aleatoriamente en concordancia con la distribución de probabilidad descrita. Con esta idea logramos mejorar los esfuerzos de localización, pero a costa de aumentar la cantidad de punteros; es decir, de aumentar el espacio utilizado para la representación de la estructura. En general, si tenemos una lista con n elementos, tendremos a lo sumo log n punteros por celda. En la mayoría de las celdas no se necesitarían tantos punteros, porque la mitad de los elementos necesitan sólo un único campo de puntero, y de los restantes elementos sólo la mitad necesitaría un puntero adicional, y así sucesivamente. De manera que, hay n/2 celdas con sólo 1 puntero, n/4 celdas con 2 punteros, n/8 celdas con 3 punteros; y en general, n/2i celdas con i punteros. i La skip list en sí misma se puede implementar como una estructura de registro de dos campos: Cabecera, la cual sería una celda ficticia para comenzar las listas, y Nivel, el cual sería un entero que daría el nivel más grande de cualquier celda presente actualmente en la lista. Una celda tendría, además de los campos para almacenar X e Y, una tabla de apuntadores a las siguientes celdas de ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 2 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. acuerdo a las listas en las que dicha celda participa. El tamaño de esta tabla dependería del nivel de la celda, pero dicho número no necesitaría ser almacenado en la celda. Inicialmente el Nivel de una skip list sería 0 y todos los apuntadores apuntarían a la celda ficticia final cuyo valor almacenado en el campo X sería, como ya habíamos expresado, el valor considerado “” para cualquier valor posible de X. La siguiente figura muestra la situación inicial de una skip list con máximo nivel = 3: Por ejemplo, se puede lanzar una moneda al aire, y mientras salga cara se aumenta el nivel del nodo a insertar en 1 (partiendo desde 1). Esta estructura de datos es denominada skip list. La siguiente figura muestra un ejemplo de una skip list: Búsqueda en una skip list Suponga que el elemento a buscar es X. Se comienza con la referencia superior de la cabecera. Se realiza un recorrido a través de este nivel (pasos horizontales) hasta que el siguiente nodo sea mayor que X o sea null. Cuando esto ocurra, se continúa con la búsqueda pero un nivel más abajo (paso vertical). Cuando no se pueda bajar de nivel, esto es, ya se está en el nivel inferior, entonces se realiza una comparación final para saber si el elemento X está en la lista o no. El algoritmo sería algo así: - Comenzando con los punteros del mayor nivel, seguirlos hasta que se encuentre un elemento con valor de X mayor o igual que el x buscado. - Si en dicha celda el valor de X encontrado es igual al x buscado, paramos y la búsqueda tuvo éxito; si en otro caso es mayor que el x buscado, volvemos hacia atrás un puntero y continuamos siguiendo los punteros del siguiente nivel más bajo. - Si un puntero de nivel 0 guía a un elemento con valor de X mayor que el x buscado, paramos y la búsqueda fracasó. Ejemplo: - Se empieza con el puntero más alto de la “head”. ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 3 Diseño y Análisis de Algoritmos - Dr. Eric Jeltsch F. Se continúa por ese nivel hasta encontrar un nodo con clave mayor que la buscada (o NIL), entonces se desciende un nivel y se continúa de igual forma. Cuando el avance se detiene en el nivel 1, se está frente al nodo con la clave buscada o tal clave no existe en la lista. Inserción en una skip list Se procede como en la búsqueda, manteniendo una marca en los nodos donde se realizaron pasos verticales, hasta llegar al nivel inferior. El nuevo elemento se inserta en la posición en donde debiera haberse encontrado, se calcula aleatoriamente su nivel y se actualizan las referencias de los nodos marcados dependiendo del nivel del nuevo nodo de la lista. Para insertar un elemento en una skip list necesitaríamos primero tener una rutina que genere los números aleatorios apropiados del nivel. En general, se puede contar con una función Random() disponible en la mayoría de los lenguajes de programación, la cual devuelve un número aleatorio k en el rango 0 k < 1 con distribución uniforme. Ejemplo: Primero: para insertar un nuevo elemento hay que decidir de qué nivel debe ser. • En una lista de las del “caso límite” la mitad de los nodos son de nivel 1, una cuarta parte de nivel 2 y, en general, 1/2i nodos son de nivel i. • Se elige el nivel de un nuevo nodo de acuerdo con esas probabilidades: se tira una moneda al aire hasta que salga cara, y se elige el número total de lanzamientos realizados como nivel del nodo. Distribución geométrica de parámetro p = 1/2. (En realidad se puede plantear con un parámetro arbitrario, p, y luego seleccionar qué valor de p es más adecuado). Segundo: hay que saber dónde insertar. • Se hace igual que en la búsqueda, guardando traza de los nodos en los que se desciende de nivel. Se determina aleatoriamente el nivel del nuevo nodo y se inserta, enlazando los punteros convenientemente. En la dirección dada más abajo, encontraran una buena simulación de la inserción de una clave, en este caso 11, aunque la probabilidad no aparece mencionada, sino que se genera aleatoriamente. ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 4 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. http://eupt2.unizar.es/asignaturas/itig/estructuras_de_datos/temario/Skip-Lists.html Borrado en una skip list La eliminación en una skip list es bastante similar a la inserción, primero se debe localizar al elemento manteniendo, como ya habíamos mencionado máximo nivel +1 apuntadores, donde punti mantendrá un puntero a la celda de más a la derecha de nivel i o superior que es anterior a la posición de la celda que contiene el x buscado. Pero, para poder utilizar la misma rutina de Localización para todas las otras rutinas, es necesario que la localización exitosa llegue indefectiblemente hasta el nivel 0 y no se detenga en niveles mayores para así poder registrar, en los punteros auxiliares, las direcciones de todas las celdas que son factibles de modificación luego de la baja. Por lo tanto, luego de que la localización tenga éxito y conociendo las direcciones de las celdas anteriores a la celda que se quiere dar de baja, en las listas de cada uno de los niveles, se procede a actualizar sólo los apuntadores de los niveles hasta el nivel de la celda a dar de baja. Por ejemplo, si se desea eliminar el elemento 10 de la lista anterior, la localización exitosa nos brindaría la siguiente información útil: y luego de la modificación estructural la skip list quedaría como: Los apuntadores que aparecen remarcados son los únicos que se modifican para reflejar la baja de la celda de nivel 1 conteniendo el valor 10. Por ser la celda de nivel 1 sólo se modifican los apuntadores hasta dicho nivel, en este caso para reflejar la baja en las listas de nivel 0 y de nivel 1. Por otra parte, al considerar el llamado dictionary problem, el que consiste de un conjunto S de nº reales, almacenados en una estructura de datos tal que las siguientes operaciones pueden ser realizadas eficientemente: _ Search(x): Dado un nº x, reportar la existencia en S. _ Insert(x): Dado un nº x, insertar en la estructura de dato. _ Delete(x): Dado un nº x, borrar o eliminar de la estructura de dato. Una de las estructuras estandares para resolver este problema son los árboles binarios balanceados. Los que soportan las operaciones antes señaladas en tiempo caso peor O(log n) y espacio de O(n). ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 5 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. Por ejemplo, si conocemos la clase de los árboles binarios balanceados, podríamos tomar como representantes a los AVL-trees, red-black-trees u otros. Lo que es claro que este tipo de estructuras de datos para mantener su performance necesitan usar el balanceo como una operación fundamental, la cual veremos que en los Red-black no resulta muy fácil de realizar, por la variedad de casos. ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 6 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. Árbol de Búsqueda Digital Un árbol de búsqueda digital es un árbol binario en donde cada nodo contiene un elemento, estos elementos son llamadas claves. El elemento a asignar al nodo está dado por la representación binaria de las claves. Para comprender mejor es necesario entender algunos conceptos. Número de bit: viene dado por el dígito binario correspondiente al nivel del nodo padre que lo contiene. Nivel: es la altura del árbol asociada a cada nodo. Supongamos que nuestro número de bits en la representación binaria de una clave leída de izquierda a derecha comienza con 1, mientras que los bits dos, tres y cuatro son 0. Entonces la llave sería 1000. Esto determina que las claves en el subárbol izquierdo de un nodo de nivel i, tiene el bit i igual a cero, mientras que los que están en el subárbol derecho de un nodo a este nivel, tiene bit i = 1. ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 7 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. Ejemplo: Se muestra un ejemplo de árbol de búsqueda digital, con las claves 1000, 0010, 1001, 0001, 1100 y 0000. a 1000 b d f c 0010 1001 e 0001 1100 0000 Fig. 1 Búsqueda: Una búsqueda en un árbol de búsqueda digital se desarrolla de la siguiente manera. Suponemos que buscamos la clave k = 0011 en el árbol de la Fig. 1, k es comparada con a, que es la clave raíz. Como k es diferente a la raíz y como el primer bit de k es 0, nos movemos al hijo izquierdo b, de la raíz. Luego, como k es distinta a la clave del nodo b y el segundo bit de k es 0, nos movemos al hijo izquierdo de b, es decir a d. Como k es diferente de la clave del nodo d, y como el tercer bit de k es 1, nos movemos a la derecha de d. El nodo d no tiene hijo derecho donde moverse. Con esto concluimos que k=0011 no está en el árbol de búsqueda. Notar que si hubiésemos querido insertar k en el árbol, entonces debería haber sido insertado a la derecha de d. Obteniendo la Fig. 2 a b d f 0000 0010 1000 c 1001 e 0001 g 0001 1100 Fig. 2 Los procedimientos del árbol de búsqueda digital para buscar, insertar y eliminar son similares a los procedimientos para árboles de búsqueda binaria, la diferencia sustantiva es que el subárbol a mover está determinado por un bit en la clave de búsqueda, más que por el resultado de la comparación de la clave de búsqueda y la clave del nodo actual. Tries El término trie viene de la palabra retrieval que significa "recuperación". Los tries, en general, tienen la propiedad de ser sufijos (o prefijos), es decir, que consideran los inicios de las cadenas de entrada para su configuración. Un trie es una estructura índice que es particularmente útil cuando las claves varían en largo. ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 8 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. Ejemplo: En Fig. 3, se representa un trie Fig. 3 Búsqueda: La búsqueda en un trie de una clave x, se realiza descomponiendo x en sus componentes caracteres y seguir las ramas determinadas por esos caracteres. La búsqueda toma los caracteres de la izquierda a derecha, uno por una. Por ejemplo, si queremos buscar la palabra GATO en la Fig. 2, descomponemos la palabra en sus caracteres (G-A-T-O), y nos situamos en la G, que es el primer carácter leído de izquierda a derecha. En el siguiente nodo seguimos por la A, que es el segundo carácter, y así, continuamos con el carácter T , para finalmente ir a la palabra que contiene, GATO. Como es la única palabra al nivel de T, podemos decir que GATO es la única palabra con sufijo GAT, no así si nos situáramos en la A, puesto que entonces, se puede decir que existen dos palabras con sufijo GA: GARRA y GATO. Tries patricia Los tries patricia, son la versión más compacta respecto de los tries, y que se basan en que sus nodos pueden etiquetarse con más de un carácter, esto es para eliminar los caminos que son comunes a nodo hoja 1 3 4 0000 2 4 0010 0001 1000 1100 1001 Fig. 4 ________________________________________________________________________ Escuela Ingeniería en Computación, Universidad de La Serena 9 Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. Trie patricia con punteros hacia atrás, es la transformación de un trie 0 1 3 4 0001 0000 2 0010 4 1100 1001 1000 Fig. 5 Búsqueda: Para buscar un elemento con clave k comenzamos en el nodo raíz y seguimos el camino determinado por los bits en k. Por ejemplo, buscar(0000), significa comenzar la inspección desde el nodo raíz, para luego seguir por el nodo que apunta el hijo izquierdo del nodo raíz, es decir 0000. El campo número-bit en este nodo es 1, el rótulo número-bit corresponde al número de bits de la clave. Como el primer bit de k es 0, concluimos que la búsqueda se mueve el nodo con 0010. Ahora se usa el tercer bit de k, como es 0, la búsqueda se mueve al nodo con 0001. El campo número-bit ahora es 4 y el cuarto bit de k es cero, así es que seguimos al campo hijo izquierdo. Esto nos lleva a un nodo con campo de número-bit menor que el nodo desde donde veníamos. En consecuencia, fue utilizado un puntero y luego se compararon las claves de este nodo con k, y si encontramos una concordancia, entonces la búsqueda es exitosa. Ahora, supongamos que buscamos un k = 1011. Comenzamos en el nodo raíz, la búsqueda se realiza moviéndose sucesivamente a los nodos con rótulos 0000, 1001, 1000, 1001. Se compara k con 1001. Como k no es igual a 1001, concluimos que no hay elemento con esta llave. Ejemplo: Dado el árbol patricia el cual posee las claves (00000, 00010,00101,01011,01101). Buscar(00010), como la segunda posición en el bit es 0, nos indica que nos movemos hacia el hijo izquierdo del nodo. Consideramos el siguiente número de bits (Skip), el cual es mayor.e nivel, el Skip es 3 por lo cual nos fijamos en el tercer bit, que tiene valor 0 y nos indica nuevamente que debemos movernos a la izquierda del nodo respecto del que estabamos. En este nivel el Skip es 4 por lo que nos fijamos en la cuarta posición de la clave buscada el cual tiene valor 1, lo que nos invita a desplazarnos hacia el hijo derecho del nodo el cual estamos visitando. Aquí nos detenemos a fijarnos en el Skip existente en este nodo ( que es 2 y el anterior era 3), lo que obviamente nos dice que es menor, y solo cuando ocurre esta situación se comparan las claves de los nodos, es decir ________________________________________________________________________ 10 Escuela Ingeniería en Computación, Universidad de La Serena Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. en este caso: If(2<3) true Comparar 00010 = =00010 (true). Por lo cual tuvimos éxito en la búsqueda, si la comparación diera resultado falso será por la no existencia del nodo en el trie patricia. Arboles Interval Son un caso particular de los árboles Red-Black, que se usan por lo general para representar sucesos que suceden a través del tiempo. Ejemplo: Tenemos un estudio realizado a 10 ardillas de laboratorio en sus respectivas jaulas, las cuales cuentan con sus ruedas de entrenamiento. El estudio consta de observar durante 30 minutos a cada una de las ardillas, y obtener el intervalo de tiempo en el cual ellas juegan en su rueda. Los resultados obtenidos son los siguientes: Al = [0,3] , significa que la 1º ardilla usó su rueda entre los 0 y los 3 minutos. A2 = [16,21] A3 = [17,19] A4 = [8,9] A5 = [15,23] A6 = [5,8] , es decir, la 6º ardilla empezó a los 5 minutos y terminó a los 8 minutos A7 = [25,30] A8 = [6,10] A9 = [26,26] A10 = [19,20] Para el estudio de este tipo de árboles, debemos tener claro que son y como representar los intervalos. Un ejemplo generalizado de intervalo es el siguiente: A = [x, y], significa que nuestro intervalo A, irá desde el número natural x hasta el número y. Por ejemplo, el intervalo [10,15] contendrá los elementos {10,11,12,13,14,15}. Cabe destacar que en esta oportunidad estudiaremos los intervalos cerrados, es decir tomando como elementos los límite del intervalo. Las "partes" del intervalo A = [x, y], son x será llamado límite inferior del intervalo e y será llamado límite superior de A. Cualquier intervalo ya insertado en un árbol interval, cumple con la tricotomía interval, esto es, que cumple exactamente una de las siguientes propiedades. Siendo A y B Intervalos: a) A y B se traslapan (esto significa que A ∩ B ≠ Ø) b) High[A] < Low[B] c) High[B] < Low[A] a) b) A B ________________________________________________________________________ 11 Escuela Ingeniería en Computación, Universidad de La Serena Diseño y Análisis de Algoritmos c) Dr. Eric Jeltsch F. B A En cada nodo de un Arbol Interval encontramos: El intervalo en si. Puntero al nodo padre. Puntero a los nodos hijos Izquierdo y Derecho. Valor Max El valor Max será igual al número Máximo entre el intervalo del nodo, el Máximo de su subárbol derecho y el máximo de su subárbol izquierdo. Aquí vemos un ejemplo [16, 21] 30 [0, 3] 3 [17, 30] 30 Podemos ver que en el intervalo que contiene a [0,3] se encuentra el valor max = 3, ya que es el máximo de su intervalo y no tiene subárboles. Igual para su nodo hermano. En el nodo padre encontramos el valor max = 30, ya que de entre el máximo de él (21), el máximo del subárbol izquierdo (3) y del derecho (30), éste último es el mas grande. Como los árboles Interval son un tipo especial de árboles Red-Black, debemos de recordar sus propiedades. Cada nodo puede ser Rojo o Negro. La raíz del árbol siempre es negra. Cada nodo ingresado es Rojo. Un nodo rojo no puede tener hijos de su mismo color. Todo camino desde la raíz hasta las hojas (nodos null) debe tener la misma cantidad de nodos negros. Sabemos que los árboles Red-Black son un tipo de árbol binario de búsqueda, y se insertan los nodos de la misma forma tomando como índice Low[x], teniendo presente actualizar el valor max de cada nodo. Veamos un ejemplo de inserción con los datos de las ardillas. [0, 3] 3 Búsqueda El objetivo funda mental de la búsqueda en este tipo de árboles, es encontrar el primer nodo que contenga un intervalo que traslape al intervalo buscado. Para esto, utilizamos el siguiente método: ________________________________________________________________________ 12 Escuela Ingeniería en Computación, Universidad de La Serena Diseño y Análisis de Algoritmos Dr. Eric Jeltsch F. Pedimos un intervalo a buscar. Entrando por la raíz, Low[x] deberá ser menor que el valor max en el nodo raíz, ya que de lo contrario, el intervalo buscado estaría fuera del rango que abarca nuestro árbol. Debemos preguntar si el intervalo en la raíz traslapa al intervalo buscado. De ser así, la búsqueda entrega este nodo. En caso contrario, revisamos el valor max del hijo izquierdo de la raiz. Si el max del hijo izq. es mayor que el Low del intervalo buscador significa que el rango del subárbol izquierdo es suficiente para contenerlo, entonces la búsqueda se dirige hacia el lado izquierdo. Caso contrario, tomaremos la derecha. Nuevamente preguntamos si el hijo izquierdo traslapa al intervalo buscado. En caso de que no traslape, se repite el proceso. En caso de no encontrar ningún nodo que contenga un intervalo que traslape al buscado, la búsqueda entregara null. Ejemplo: Buscaremos el Intervalo X=[22,23] Entramos por la raíz y preguntamos si el intervalo [16,21] traslapa a [22,23]. Como no lo hace, preguntamos si el valor max del hijo izq. (24) es mayor que Low del intervalo buscado (22). Como es mayor, vamos al hijo izquierdo. Preguntamos si (8,9] traslapa a [22,23]. Como no lo hace, revisamos el max del hijo Izq. (3) y preguntamos si es mayor que Low[x] (22). No es mayor, por lo tanto seguimos por la derecha. ¿[15,24] traslapa a [22,23]? Sí. La búsqueda devuelve el nodo que contiene a [15,24] Tenemos que destacar que esta búsqueda solo entrega el primer intervalo que traslapa al intervalo buscado, pero puede suceder que bajo el intervalo entregado existan otros nodos que contengan intervalos que también traslapen, por lo tanto si queremos obtener todos los intervalos que trasladan, debemos realizar una búsqueda recursiva con los subárboles derecho e izquierdo del nodo entregado. ________________________________________________________________________ 13 Escuela Ingeniería en Computación, Universidad de La Serena