Algoritmica y Estructruras de Datos

download Algoritmica y Estructruras de Datos

of 37

description

Ejercicios de repaso

Transcript of Algoritmica y Estructruras de Datos

Algoritmos y Estructura de DatosGUIN DE CLASES DE TEORA Pablo Nogueira Actualizado: 14 de noviembre 2011, 13:03 Este fichero es un guin que se complementa en clase con transparencias, explicaciones en la pizarra y discusiones. ndice* 1 Normativa y material * 2 Repaso de listas simplemente enlazadas * 3 Listas indexadas, enlazadas y secuencias * 4 Conjuntos, iteradores, complejidad * 5 Arboles generales y binarios * 6 Colas con prioridad, montculos y ordenacin 1 Normativa y material * Preliminares * La Gua de Aprendizaje est disponible en: 1. Aula Virtual (Gua en formato HTML) Interfaz Position* ^ ^* I | | I* Clase NodePositionList ---- U ---> Clase DNode* \* \-- U ---> Clase ElementIterator* * U: Usa. I: Implementa. * Omitimos las clases de excepciones 'EmptyListException', 'InvalidPositionException' y 'BoundaryViolationException'. * Interfaz Position * Una posicin es una abstraccin de un nodo. Slo nos interesa el elemento almacenado: mtodo 'element'. * Se define este interfaz por separado porque se utilizar con otros TADs ms adelante. * Clase DNode * [Transparencia 4] * Implementacin de nodos. Implementa 'Position'. * Referencias al nodo aterior ('prev') y siguiente ('next') que son de clase 'DNode' y no de tipo 'Position'. Por qu? * Mtodos: constructor, "getters" y "setters", mtodo 'element' e 'InvalidPositionException'. * El mtodo 'element' lanza una excepcin si el nodo no est encadenado. Por qu? Tiene sentido hacer esta comprobacin en esta clase en vez de en la implementacin de la cadena enlazada? * EJERCICIO: Comparar con la *Clase Node del primer tema. * EJERCICIO: Podramos hacer ms comprobaciones 'InvalidPositionException'? * EJERCICIO: Podramos usar 'DNode' en la implementacin de una cadena simplemente enlazada? En caso positivo, cmo se hara? * Interfaz PositionList * Extiende Iterable *6.3 Iterators. * Vemos los mtodos en clase. * Excepciones: * 'EmptyListException': lanzada cuando el mtodo no puede aplicarse sobre la lista es vaca. * 'InvalidPositionException': lanzada cuando la posicin pasada al mtodo no es vlida (legalidad). Ejemplo: objeto null. * 'BoundaryViolationException': lanzada cuando la posicin pasada al mtodo no es es la adecuada (control de rango). Ejemplo, 'list.prev(list.first())' lanzar la excepcin. Ms sobre esto en *Clase NodePositionList. * Los mtodos 'first' y 'last' no declaran que lancen excepciones 'EmptyListException', al contrario que en *Listas simplemente enlazadas. Esto no es correcto desde el punto de vista de la usabilidad de la librera: el usuario de la librera no sabe que esos mtodos lanzan excepciones y por tanto no los invocar dentro de un bloque try-catch con el que podra capturar las excepciones. * Insercin arbitraria: mtodos 'addBefore' y 'addAfter'. * EJERCICIO: Tiene sentido un mtodo 'add(Position p, E e)'? * Clase NodePositionList * [Transparencias 4,5,7] * ERRORES: 'insertAfter' debera ser 'addAfter'. * Implementacin de listas de posiciones mediante una cadena doblemente enlazada. * Nodos especiales 'header' y 'trailer' que no almacenan elementos. * El constructor crea una lista vaca que consiste en un header y trailer encadenados. * El mtodo protegido 'checkPosition' comprueba si una posicin (nodo) es vlida. Una posicin es invlida cuando [Libro, p245]: * Es 'null'. * Es el nodo 'header' o 'trailer' * No est encadenado. (Ntese el "downcasting" en [Cdigo, lnea 37] y la clusula 'catch'). Otras posibles comprobaciones que no se hacen aqu: * Es un nodo de otra lista. Se podra comprobar? * Control de rango, p.ej, 'L.prev(L.first())'. Estas comprobaciones se hacen en los mtodos pertinentes. * Los mtodos 'first' y 'last' lanzan 'EmptyListException' (lo cual es correcto) aunque el interfaz no lo declara. La excepcin est declarada en el fichero EmptyListException.java del paquete net.datastructures. * Ilustramos los mtodos 'addAfter', 'addBefore' y 'remove'. * El mtodo 'positions' crea una nueva 'NodePositionList' con las posiciones de la lista y el mtodo 'iterator' crea un 'ElementIterator'. (De momento no entramos en los detalles de estos dos mtodos. Volveremos a ellos cuando veamos *6.3 Iterators y *Ejemplos de iteradores. * Hay otros mtodos internos 'isFirst', 'isLast', 'swapElements' que no son usados en otra parte de la librera. * Los mtodos de serializacin a cadenas los veremos en *6.3 Iterators. * Anlisis de las listas de posiciones * Complejidad * Complejidad en el caso peor de todos los mtodos, excepto de iteracin: O(1). * EJERCICIO: Dado un elemento, si no se tiene su posicin entonces habra que buscarla. Implementar el mtodo 'public Position getPos(E e)' que devuelve la primera posicin en la lista que contiene el elemento. Cul sera la complejidad de dicho mtodo? * Grado de abstraccin conseguido * La implementacin de nodos queda oculta a los usuarios del interfaz 'PositionList'. No es necesario crear objetos de la *Clase DNode, basta con usar los mtodos del interfaz 'PositionList', guardando si necesario en variables de tipo 'Position' algunos objetos intermedios. * PositionList plist = new NodePositionList();* Position p;* * plist.addFirst(50); // (50)* plist.addLast(42); // (50,42)* p = plist.first();* plist.addAfter(p,41); // (50,41,42)* plist.set(p,40); // (40,41,42)* /* Aade a la lista un elemento e1 inmediatamente antes de otro* * elemento e2 slo si este ltimo est en la lista.* */* public void addBeforeElement(PositionList list, E e1, E e2) {* if (!list.isEmpty()) {* Position p = list.first();* int n = 1; // Estamos en en primer nodo* while (p.element() != e2 && n 0, B -> 1, C -> 2, D -> 3* * Conjunto {A,D} como vector de bits:* * 0 1 2 3* |------+-------+-------+------|* | true | false | false | true |* |------+-------+-------+------|* En Java, los ndices de los vectores slo pueden ser enteros 'int'. * Para poder utilizar elementos como ndices su tipo debe ser finito numerable, en correspondencia con un subconjunto finito de los enteros. En Java: tipos enumerados. * En Java, todo tipo enumerado hereda de la clase 'Enum'. Ejemplo: * public enum Dias { Lun, Mar, Mie, Jue, Vie, Sab, Dom; }El enumerado 'Dias' es una clase que hereda de 'Enum'. Las constantes enumeradas (p.ej., 'Dias.Lun') son objetos de la clase. El compilador genera automticamente el cdigo para los mtodos heredados de la clase 'Enum', entre los que destacamos: * Mtodo 'ordinal' que devuelve el ndice de la constante enum dentro de la enumeracin. Ejemplo: 'Dias.Lun.ordinal()' devuelve 0 y 'Dias.Dom.ordinal()' devuelve 6. * Mtodo 'equals'. * Mtodo 'toString'. Ejemplo: 'Dias.Lun.toString()' devuelve la cadena de caracteres "Lun". * Mtodo 'getDeclaringClass' que devuelve el objeto clase (API de reflexin) al que pertenece la constante enum. Ejemplo: 'Dias.Lun.getDeclaringClass()' devuelve un objeto de clase 'Class'. * Problema con la clase 'Enum': no ofrece un mtodo que devuelva cuntas constantes tiene el enumerado (el "tamao" del enumerado). Se puede obtener ese valor usando la API de reflexin. * Estudiaremos en detalle esta implementacin en *Una librera de conjuntos. * La JCF ofrece una implementacin: 'EnumSet'. * Complejidad (lo veremos en *Una librera de conjuntos): Mtodo Complejidad add O(1) remove O(1) member O(1) union O(n) 0) { Position p = list.first(); try { while (true) { System.out.print(p.element() + " "); p = list.next(p); } } catch (BoundaryViolationException e) { }}* Ejemplo 3: Mostrar por pantalla los elementos almacenados en un objeto 'set de tipo *Interfaz Set: Slo se dispone del mtodo observador 'member' con lo que no es posible recorrer el conjunto en general usando los mtodos del interfaz. S es posible recorrer conjuntos finitos 'BitVectSet' usando los mtodos del interfaz 'Set', siempre que dispongamos de un valor del tipo enumerado 'E' sobre el que poder usar reflexin. Supongamos que en la variable 'e' tenemos dicho valor: if (set.size() > 0) { E [] v = e.getDeclaringClass().getEnumConstants(); for (int i=0; i < v.length; i++) if (set.member(v[i])) System.out.print(v[i] + " ");}Finalmente, tambin es posible hacer un recorrido "desde dentro" de la implementacin de la clase 'BitVectSet': public class BitVectSet implements Set { protected boolean bv[]; protected E [] enumConstants; // Guardamos el vector de constantes // aqu cuando se invoque 'add'. ... public static void print() { for (int i = 0; i < bv.length; i++) if (bv[i]) System.out.println(enumConstants[i] + " "); }}* Concepto de iterador * En los ejemplos de la seccin anterior el cdigo del recorrido es especfico a cada TAD. Se usan mtodos especficos de su interfaz y no siempre se puede usar el mismo estilo de bucle. * El iterador es un patrn de diseo ('design pattern') para recorrer linealmente los elementos de TADs que almacenan elementos (ojo, no todos los TADs son colecciones de elementos) tal que: 1. El recorrido no se hace a travs de los mtodos del interfaz del TAD sino a travs de los mtodos de un interfaz nico 'Iterator'. 2. Permite usar el mismo estilo de bucle para todos los TADs. * Conceptualmente, un objeto iterador mantiene una referencia a un objeto elemento del TAD, referencia que llamamos cursor. Que dicha referencia sea un atributo del objeto iterador depende de cmo se defina el iterador. Concretamente, en lo tocante a TADs se pueden definir objetos iterador para: 0. Interfaces de TADs. En este caso los iteradores se implementan usando los mtodos del interfaz y pueden usarse para iterar sobre objetos de cualquier clase que implemente en interfaz. 1. Clases concretas que implementan interfaces de TADs. En este caso los iteradores pueden implementarse usando mtodos del interfaz del que heredan las clases, o los atributos internos de las clases, o ambos. Estos iteradores nicamente pueden usarse para iterar sobre objetos de las clases concretas. Por ejemplo, dada la clase 'C' que implementa el interfaz 'I', el iterador definido para objetos de 'C' slo puede usarse sobre objetos de dicha clase. En la seccin *Cmo iterar sobre TADs detallaremos las dos alternativas. La primera alternativa es ms deseable desde el punto de vista de abstraccin y reusabilidad. En ese caso el cursor s ser un atributo del iterador. Pero puede no ser posible realizarla porque no hay suficientes mtodos observadores (como en el Ejemplo 3 que hemos visto en la seccin *Motivacin de los iteradores) o porque la iteracin usando mtodos del interfaz es ineficiente. * [Transparencia 2]: "Extends the concept of position". [Libro, p254]: "Thus, an iterator extends the concept of the position ADT we introduced in Section 6.2. In fact, a position can be thought of as an iterator that doesn't go anywhere". Tiene realmente sentido entender 'Position' como un iterador sobre una coleccin que contiene un nico elemento? * Interfaces Iterator e Iterable * Declaraciones * El interfaz 'Iterator' declara los mtodos que debe implementar todo objeto iterador. El interfaz est en 'java.util.Iterator'. * public interface Iterator {* public E next() ; /* obligatorio implementarlo */* public boolean hasNext() ; /* obligatorio implementarlo */* public void remove() ; /* no obligatorio, problemtico! */* }* El interfaz 'Iterable' declara un mtodo 'iterator' que devolver un objeto iterador. Este interfaz es la pieza que nos permitir asociar iteradores a un TAD. El interfaz est en 'java.lang.Iterable'. * public interface Iterable {* public Iterator iterator() ;* }* Significado de los mtodos de Iterator * Los nombres de los mtodos de 'Iterator' no parecen intuitivos a primera vista. Detallamos su significado: * 'hasNext' indica si el cursor del iterador referencia un elemento ("tienes un elemento?"). * 'next' devuelve el elemento al que referencia el cursor y avanza el cursor ("dmelo y pasa al siguiente"). * Los mtodos se usan en bucles, donde 'hasNext' es la condicin que se comprueba antes de invocar 'next'. Retomando el ejemplo de mostrar elementos de un TAD por pantalla *Motivacin de los iteradores. En pseudocdigo: * while ("tienes algo?") {* elem = "dmelo y pasa al siguiente";* System.out.print(elem + " ");* }Veamos el Ejemplo 1 y 2 de *Motivacin de los iteradores con iteradores. El mismo cdigo de bucle vale para cualquier TAD iterable, la nica diferencia est en qu objeto iterador se usa: Iterator it = new ...; /* invocar constructor de iterador concreto */ E e = it.next();while (it.hasNext()) { System.out.print(e + " ");}en este ejemplo la variable 'e' es redundante y puede omitirse: Iterator it = new ...;while (it.hasNext()) System.out.print(it.next() + " ");}* Al crearse el objeto iterador: * Si el TAD est vaco: * hasNext() devuelve false.* next() lanza NoSuchElementException.* Si el TAD no est vaco: * hasNext() devuelve true.* next() devuelve el primer elemento y avanza el cursor* al segundo elemento.* Conceptualmente, el cursor es null cuando el TAD est vaco o cuando 'next' devuelve el ltimo elemento de la iteracin ('next' deja el cursor a null pues no hay elemento siguiente). * Ahora podemos entender los nombres de los mtodos: el cursor referencia el siguiente elemento que todava no ha devuelto 'next', por tanto: * 'hasNext' significa "hay un siguiente elemento en el cursor?" * 'next' significa "dame ese siguiente elemento en el cursor y avanza el cursor". * Cul es el primer elemento y cmo se avanza (orden de elementos) depende del TAD. En el caso de las listas el orden lo determina la posicin que ocupa el elemento. En el caso de conjuntos de enumerados el orden lo determina la posicin que ocupa la constante enumerada en la declaracin 'enum'. Etc. * Ejemplo: Tenemos un TAD con 2 elementos 'A' y 'B', * Inicialmente el cursor referencia al primer elemento porque el TAD no es vaco. * {A,B}* ^* |* cursor* * hasNext() devuelve true.* next() devuelve A y avanza el cursor a B,* * * {A,B}* ^* |* cursor* * hasNext() devuelve true.* next() devuelve B y avanza el cursor a null,* * {A,B}* * cursor -> null* * hasNext() devuelve false.* next() lanza NoSuchElementException.* El mtodo 'remove' del iterador no se describe en detalle en [Libro, p254, 'Simple Iterators in Java']. No es obligatorio implementar la funcionalidad completa. * Incompleto: slo lanza 'UnsupportedOperationException' (cuando es invocado). Este es el procedimiento habitual en la JCF para mtodos incompletos invocados. * Completo: debe borrar del TAD el elemento al que referencia el cursor, lanzando 'IllegalStateException' si el cursor es null. * Al iterar un TAD, ste slo puede modificarse (borrar elementos) con el 'remove' del iterador. No se deben usar mtodos de insercin o borrado del TAD durante la iteracin pues el resultado de la misma puede ser impredecible. * Cmo iterar sobre TADs * Declaraciones y clases El siguiente procedimineto permite definir iteradores para TADs usando los mtodos de sus interfaces (alternativa 1 vista en *Concepto de iterador). 0. Hacer que el interfaz del TAD extienda 'Iterable': 1. import java.util.Iterator;2. public interface TAD extends Iterable {3. 4. ... /* mtodos del TAD */5. 6. public Iterator iterator() ;7. public Iterable elements() ; /* no obligatorio pero til */8. }* El mtodo 'iterator' se hereda de 'Iterable'. * El mtodo 'elements' es nuevo, no se hereda de 'Iterable'. No es obligatorio declararlo, pero es til: 'snapshot' [Libro, p257], persistencia y mutabilidad (borrado, [JP'05, p102]). Hablaremos de l ms adelante. Que un TAD sea "iterable" (su interfaz extiende 'Iterable') garantiza que se tendr un iterador (objeto que implementa 'Iterator') que se obtendr invocando 'iterator' y se usar para recorrer todas las implementaciones del TAD. 9. Implementar una clase iterador para el TAD: 10. import java.util.Iterator;11. public class TADIterator implements Iterator {12. TAD tad;13. E cursor;14. public TADIterator(TAD t) {15. tad = t;16. ... /* cdigo aqu que inicializa el valor del cursor */17. }18. public boolean hasNext() { /* cdigo aqu */ }19. public E next() { /* cdigo aqu */ }20. public void remove() { /* cdigo aqu */ }21. }* El constructor toma como parmetro el objeto 't' (que implementa el interfaz 'TAD') sobre el que iterar. Lo almacena en un atributo interno de la clase. * Se deben implementar 'next', 'hasNext' y 'remove' utilizando nicamente los mtodos del interfaz 'TAD' sobre el atributo 'tad' (slo sabemos que implementa el interfaz 'TAD'). Si esto no es posible entonces debemos asociar el iterador con la implementacin del TAD (ver ms abajo). 22. Implementar el mtodo 'iterator' en todas las implementaciones del interfaz 'TAD': 23. import java.util.Iterator;24. public class TADImpl1 implements TAD {25. ...26. public Iterator iterator() { return new TADIterator(this); }27. }28. 29. public class TADImpl2 implements TAD {30. ...31. public Iterator iterator() { return new TADIterator(this); }32. }33. 34. ...35. Resumen: 36. TAD ----- E -----> Iterable ------ U ----> Iterator37. ^ ^ ^38. I | | I | I39. | \ |40. | TADImpl2 ------------- U ---------------> TADIterator41. TADImpl1 --------------------- U --------------/42. 43. E: Extiende.44. I: Implementa.45. U: Usa.Todas las implementaciones de 'TAD' pueden usar 'TADIterator' siempre que implementen el mtodo 'iterator' como se ha indicado en el punto 3. 46. EJEMPLO: IterableIndexList.zip (disponible en el Aula Virtual). En este cdigo redefinimos el *Interfaz IndexList para poder iterar sobre listas indexadas. Se implementa el iterador 'ElementIterator'. 47. EJEMPLO: en los ficheros de la librera net.datastructures del libro PositionList.java, ElementIterator.java y NodePositionList.java. * 'ElementIterator' implementa 'Iterator' para 'PositionList'. (Comprese el cdigo con el 'ElementIterator' de IterableIndexList.zip. Curiosamente el cursor no referencia un elemento de tipo 'E' sino una posicin 'Position', lo cual no concuerda exactamente con el patrn que hemos descrito. Sin embargo, el cursor podra referenciar el elemento llevndose cuenta en un atributo nuevo la posicin de ste. Comparar con IterableIndexList.zip donde el cursor referencia un elemento de tipo 'E' y otro atributo lleva cuenta del ndice que ocupa el elemento en el vector. * 'PositionList' extiende 'Iterable'. (El mtodo 'positions' es lo que hemos llamado 'elements' en *Declaraciones y clases.) * 'NodePositionList' implementa el mtodo 'iterator' [Cdigo, lnea 160] y usa el iterador en los mtodos 'toString' [Cdigo, lnea 216] y 'forEachToString' [Cdigo, lnea 202]. *Ejemplos de iteradores * Iteracin con bucles 'while' y 'for' * Bucles WHILE: * TAD tad = new ... ;* E e;* Iterator it = tad.iterator();* while(it.hasNext()) {* e = it.next();* /* se hace algo con 'e' */* }* Bucles FOR: * TAD tad = new ... ;* E e;* for (Iterator it = tad.iterator(); it.hasNext(); ) {* e = it.next();* /* se hace algo con 'e' */* }* Se pueden declarar varios iteradores simultneamente sobre un mismo TAD: * TAD tad = new ... ;* E e;* for (Iterator it1 = tad.iterator(); it1.hasNext(); ) {* e = it1.next();* for (Iterator it2 = tad.iterator(); it2.hasNext(); it2.next())* System.out.print(e);* }* EJERCICIO: Cul sera la salida del cdigo anterior? * Iteracin con bucle 'for-each' de Java * [Libro, p256], [JP'05, p50] * http://java.sun.com/docs/books/jls/third_edition/html/statements.html#24588 * Patrn de sintaxis: * for (type elem : expr) { stmts }Donde: * La variable 'elem' tiene tipo 'type' y no ocurre en 'expr'. * La expresin 'expr' tiene tipo 'Iterable' o tipo array de T, con T un subtipo o un "boxing" de 'type'. * Semntica informal: 1. Cuando 'expr' es de tipo Iterable el bucle se lee como "ejecuta 'stmt' para todo elemento 'elem' del iterable 'expr'". Se trata de una abreviatura de: 2. for (Iterator it = expr.iterator(); it.hasNext(); ) {3. type elem = it.next();4. stmts5. }donde 'it' es una nueva variable que no ocurre en 'stmts'. (Ntese que 'elem' se declara despus de usarse 'expr' y por eso 'elem' no puede ocurrir en 'expr'.) Si no hay elementos para iterar ('hasNext' devuelve falso) no se ejecuta el cdigo del for-each. La variable 'elem' puede declararse localmente dentro del bloque del bucle 'for': "A local variable declaration can also appear in the header of a for statement (14.14). In this case it is executed in the same manner as if it were part of a local variable declaration statement" http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.4 Dada la equivalencia entre 'for' y 'while: { Iterator it = expr.iterator(); type elem; while(it.hasNext()) { elem = it.next(); stmts }}6. Cuando 'expr' es de tipo array de T se trata de una abreviatura de: 7. for (int j = 0; j < expr.length; j++) {8. type elem = v[j];9. stmts10. }Donde 'j' es una nueva variable que no ocurre en 'stmts'. Ejemplo de uso de for-each: public int sumaArray(int [] v) { int suma = 0; for (int i : v) suma += i; return suma;}* Dentro de 'stmt' no se tiene acceso al iterador (a una variable que referencie el objeto iterador), slo al elemento 'elem'. No se pueden invocar 'next', 'hasNext', ni 'remove'. Esto significa que en principio 'for-each' debe usarse para iterar sobre todos los elementos (de ah su nombre "para-cada"). Para iterar slo sobre algunos elementos (por ejemplo, aquellos que cumplan una condicin) debe usarse el iterador directamente *Ejemplos de iteradores. * Otras alternativas * Otra forma de poder iterar sobre un TAD (utilizada tambin alguna vez en el cdigo del libro) consiste en declarar el mtodo 'iterator' como mtodo propio de 'TAD' sin extender de 'Iterable': * import java.util.Iterator;* public interface TAD {* ...* public Iterator iterator();* }Problema: 'TAD' no es 'Iterable'. No podemos usar el iterador en contextos donde se espera un objeto iterable, por ejemplo, no podemos hacer *Iteracin con bucle 'for-each' de Java. * Tambin se puede asociar el iterador a una implementacin (clase) y no al interfaz: * Cdigo del interfaz 'TAD': * public interface TAD /* no extiende Iterable */ { ... }* Cdigo de una implementacin: * public class TADImpl1 implements TAD {* ...* public Iterator iterator() { return new TADImpl1Iterator(this); }* }* Cdigo del iterador asociado a la implementacin 'TADImpl1': * public class TADImpl1Iterator implements Iterator {* TADImpl1 tadImpl1;* E cursor;* public TADImpl1Iterator(TADImpl1 t) {* tadImpl1 = t;* ... /* cdigo aqu que inicializa el valor del cursor */* }* public boolean hasNext() { /* cdigo aqu */ }* public E next() { /* cdigo aqu */ }* public void remove() { /* cdigo aqu */ }Puede no ser necesario definir el cursor explcitamente como un atributo del iterador. El constructor toma como parmetro un objeto de la clase 'TADImpl1', no un objeto de tipo 'TAD'. Los mtodos 'hasNext' y 'next' pueden implementarse o bien usando los mtodos del interfaz 'TAD' (porque 'TADImpl1' implementa los mtodos de 'TAD') o bien accediendo directamente a los atributos de 'TADImpl1' si son pblicos. Alternativamente, podra definirse 'TADImpl1Iterator' como una clase anidada no esttica ('non-static') dentro de 'TADImpl1', pudiendo la primera acceder a los atributos privados de la segunda. (Consultar en la [JLS'3E] los conceptos de clases 'nested', 'inner', 'local', y 'anonymous', as como la siguiente URL: http://download.oracle.com/javase/tutorial/java/javaOO/nested.html * El iterador 'TADImpl1Iterator' slo puede usarse con objetos de clase 'TADImpl1'. No puede usarse con objetos de otras clases que implementen 'TAD'. Esas clases deben tener su propio iterador. * TAD Iterator* ^ ^ ^* I | | I | I* | \ |* | TADImpl1 ------------- U ---------------> TADImpl1Iterator* TADImpl2* * E: Extiende.* U: Usa.* I: Implementa.* Un comentario sobre la adecuacin de implementar iteradores en las clases. Tomemos el ejemplo de los conjuntos como ilustracin *Iterfaz Set. En un conjunto no importa el orden de elementos. La iteracin sobre un conjunto a travs del interfaz no es posible, como hemos visto en el Ejemplo 3 de *Motivacin de los iteradores. Podramos haber definido un mtodo 'iterator' directamente en el interfaz, pero al no haber ningn tipo de orden la iteracin de conjuntos no puede ser determinista: dos conjuntos iguales pueden tener iteraciones distintas. Por ejemplo, sean 's' y 'r' variables de tipo *Interfaz Set donde 's' almacena el conjunto resultado de insertar 3 en el conjunto con slo el elemento 4, mientras que 'r' es el resultado de insertar 4 en el conjunto con slo el elemento 3. Se tiene que 's.equals(r)' y 'r.equals(s)' son ciertos (pues 'equals' implementa la igualdad de conjuntos). Supongamos que tanto 's' como 'r' estn implementados mediante listas de posiciones *Conjuntos mediante listas, concretamente como objetos de una clase 'ListSet' que almacena los elementos del conjunto en un atributo de clase 'NodePositionList'. Supongamos adems que el mtodo 'add' de los conjuntos se implementa insertando el elemento al principio (mtodo 'addFirst') de dicha la lista de posiciones. Las listas de 's' y de 'r' son diferentes e iterar sobre ellas produce distintos recorridos: 3,4 para 's' y 4,3 para 'r'. En general, se puede definir un iterador para casi cualquier TAD de elementos estipulando que el iterador puede ser no-determinista (recorre los elementos de forma arbitraria segn cmo estn disponibles en la implementacin del TAD). Pero entonces la computacin que se realice con los elementos iterados debe dar el mismo resultado para cualquier posible permutacin de todos los elementos. Por ejemplo, la suma de elementos dara el mismo resultado para cualquier recorrido. Sin embargo mostrar elementos por pantalla dara resultados diferentes. En el caso de conjuntos finitos de enumerados 'BitVectSet' el dominio de elementos 'E' est ordenado por el orden en el que aparecen las constantes enumeradas, con lo que la iteracin sobre dos conjuntos 'BitVectSet' iguales siempre recorrer los mismos elementos en el mismo orden (como muestra el ltimo fragmento de cdigo del Ejemplo 3 de la seccin *Motivacin de los iteradores). Por tanto, las implementaciones pueden aadir ms condiciones sobre los elementos que pueden hacer factible la iteracin determinista en su caso. Veremos ms ejemplos cuando veamos TADs con orden. * Ejemplos de iteradores * Mostrar los 'n' primeros elementos de un array de caracteres 'v': * Usando for-each: * for (char c : v)* if (n>0) {* System.out.print(c + " ");* n--;* }Se recorre todo el array. Se podra usar 'break', pero es un error de estilo: for (char c : v) if (n == 0) break; if (n>0) { System.out.print(c + " "); n--; }Un principio de programacin estructurada [Bruce J. MacLennan, "Principles of Programming Languages: Design, Evaluation and Implementation"]: "Structure: The static structure of the program should correspond in a simple way to the dynamic structure of the corresponding computations." Corolario para bucles: Debe salirse fuera de un bucle nicamente cuando la condicin sea falsa, tenindose as un nico punto de salida a tener en cuenta en el diseo y demostracin de propiedades (p.ej. invariantes) del bucle. No deben usarse 'break', 'continue' o 'return' para salir de un bucle. Desafortunadamente, el cdigo del libro no sigue este principio. La solucin idnea a este problema se muestra en el siguiente punto. * Los arrays no tienen iteradores: * for (Iterator it = v.iterator(); n>0 && it.hasNext(); n--)* System.out.print(it.next() + " ");Este cdigo tiene dos errores: 0. Los tipos base ('char') no se pueden pasar como parmetros genricos, tendra que usarse 'Character' y el autoboxing. 1. Los arrays no extienden 'Iterable' y por tanto no tienen un mtodo 'iterator'. Hay que usar ndices (solucin idnea): for (int i = 0; n > 0 && i < v.length(); i++, n--) System.out.print(v[i] + " ");* Ejemplos en PositionList.java y NodePositionList.java: * NodePositionList implementa el mtodo 'iterator' [Cdigo, lnea 160] y usa el iterador en los mtodos 'toString' [Cdigo, lnea 216] y 'forEachToString' [Cdigo, lnea 202]. * Obsrvese que en el bucle 'for-each' de ste ltimo se lleva cuenta del nmero de elementos ya iterados en una variable auxiliar para poder determinar cundo concatenar la coma, ya que no puede invocarse 'hasNext' dentro del bucle *Iteracin con bucle 'for-each' de Java. * Ms ejemplos en *9.1.2 A Simple List-Based Map Implementation. * EJERCICIO: Implementar la clase 'IterableArrayIndexList' que implemente el interfaz 'IndexList' de IterableIndexList.zip (fichero disponible en el Aula Virtual) y adems implemente los mtodos estticos 'toString' y 'forEachToString' similares a los de 'PositionList'. * 6.3.4 List Iterators in Java * [Transparencia 6, iterators.pdf] [Libro, Seccin 6.3.4 'List Iterators in Java'] * En la JCF los interfaces de lista extienden 'Collection' que a su vez extiende 'Iterable'. * No existe 'Position' en JCF, los mtodos de listas usan ndices y/o elementos. * Interfaces: 'List' (usa ndices) y 'ListIterator'. Hay adems clases abstractas: 'AbstractList', 'AbstractSequentialList', etc. * Implementaciones (Java Platform SE 6): 'ArrayList', 'LinkedList', 'AttributeList', 'CopyOnWriteArrayList', 'RoleList', 'RoleUnresolvedList', 'Stack', 'Vector'. * 'Fail-fast' para mltiple iteradores cuando uno modifica la coleccin [Libro, p259]. * 4.2 Analysis of Algorithms * Material * Transparencias: analysis.pdf. * Libro: Secciones 4.1 y 4.2. La Seccin 4.1 repasa preliminares matemticos: * Funcin constante, logaritmo, lineal, n-log-n, cuadrtica, cbica, polinomial y exponencial. * Sumas aritmticas y geomtricas. * Razones de crecimiento. * Funciones techo ('ceiling') y suelo ('floor'). * Resumen de ideas * La complejidad, eficiencia, o coste de un programa es la medida del tiempo de ejecucin y del uso de memoria (espacio). * La complejidad en tiempo y la complejidad en espacio suelen estar reidas: se ahorra en tiempo a costa de consumir ms espacio y se ahorra espacio a costa de tardar ms tiempo. Por ejemplo *Conjuntos mediante listas. * Cmo se mide la complejidad de un programa? 0. [Transparencias 3-4] Anlisis experimental. Hay que escribir el programa en un lenguaje de programacin concreto. Requiere anlisis estadstico (con buen muestreo de datos de entrada) y probabilstico (distribucin de dichos datos). Resultados dependen del entorno de ejecucin (compilador, sistema operativo, arquitectura, entorno dinmico, etc.) 1. [Transparencia 5 en adelante] Anlisis terico. Se fundamenta en las siguientes ideas: 2.1 Se estudian algoritmos en pseudocdigo que son analizados independientemente de lenguajes y arquitecturas concretos. Estamos interesados en la complejidad "intrnseca" del algoritmo, independiente del entorno de ejecucin. El pseudocdigo es una notacin que abstrae (alto nivel) las particularidades de los lenguajes de programacin (ojo, de un paradigma de programacin [Transparencia 8]). La complejidad de las operaciones primitivas de un algoritmo descrito en pseudocdigo es proporcional (funcin constante) a la complejidad real de su implementacin en un lenguaje de programacin concreto ejecutndose en entorno concreto. Por tanto, la complejidad del algoritmo en pseudocdigo es proporcional (funcin constante) a la complejidad de su implementacin. Dicho de otro modo, los factores a tener en cuenta en una implementacin real afectan la complejidad terica en un factor constante [Transparencia 14]. 2.2 Se estudian programas ya implementados (p.ej., Java) asumiendo que la complejidad de las operaciones son proporcionales (funcin constante) a las de los algoritmos en pseudocdigo. 2.3 La complejidad (tiempo o espacio) de un algoritmo es una funcin del tamao de la entrada ('input'). Esa funcin es o bien constante o bien crece en proporcin al tamao de la entrada. (Se asume que no puede decrecer al crecer el tamao de la entrada.) El tamao de la entrada puede crecer indefinidamente. Daremos una medida de complejidad asinttica en *Notacin O() mediante una funcin del coste en tiempo (o espacio) cuando el tamao de la entrada tiende a infinito. 2.4 El estudio del caso peor nos da unos resultados ms precisos y con menor variabilidad que los estudios de casos medios y los experimentales. Los algoritmos eficientes en el caso peor lo sern tambin en los otros casos. * En la prctica se necesita tanto el anlsis terico como el experimental: "In theory there is no difference between theory and practice. In practice there is." (Yogi Berra) * En lo tocante a TADs en Java, las medidas de complejidad estn asociadas a los mtodos de las clases (implementaciones), no a los interfaces. Un interfaz describe simplemente la cabecera del mtodo (el qu) y no la implementacin (el cmo). Un interfaz puede exigir que ciertos mtodos se implementen con una complejidad determinada, forzando as una representacin y familia de algoritmos para el TAD. Finalmente, la declaracin de los mtodos en los interfaces puede sugerir o descartar ciertas implementaciones. * Notacin O() * Intuicin: establecer la complejidad de una funcin del tamao de la entrada indicando otra funcin proporcional que la acota asintticamente. * Definicin Matemtica: Sean f y g dos funciones de naturales a reales. Se dice que f(n) es del orden de g(n), o f(n) es O(g(n)), si existe una constante real c>0 y una constante natural n0 >=1 tal que f(n) = n0 * Intuicin: a partir de un valor n0 de la entrada, la funcin g(n) acota los valores de la funcin f(n) cuando n crece indefinidamente. * Ejemplos [Transparencia 20]. * Definicin en Ingls: [Transparencias 21 y 30]. * Anlisis de complejidad: se calcula f(n) y se determina el "mnimo" g(n) [Transparencias 20-23] [Libro, p173, 'Characterizing Functions in Simplest Terms']. * Se ignoran factores constantes y de orden inferior [Transparencias 20-23]. Pero CUIDADO: [Libro, p176, 'Some Words of Caution']: "we should at least be somewhat mindful of the constant factors and lower order terms we are "hiding"". Ejemplo: O(10100*n). * Escala tpica de complejidad (de menor a mayor): 0. Constante: O(1) 1. Logartmica: O(log n) 2. Lineal: O(n) 3. N-Log-N: O(n log n) 4. Cuadrtica O(n2) 5. Cbica: O(n3) 6. Polinomial: O(nm) 7. Exponencial: O(2n) .. O(mn) * Hay una relacin entre log2(x) y 2x. Recordad: log2(x) = y 2y = x. Son funciones inversas: * log2(2x) = x * 2log2(x) = x * Notaciones Omega y Theta * [Transparencias 29-31] [Libro, p174] * Complejidad: algunos comentarios y ejercicios * Es realista que el coste de la evaluacin de expresiones y del indexado en arrays sea constante? [Transparencia 11]. (El indexado de un array se hace en base a expresiones y es asimismo una expresin.) * Comparar [Transparencia 11] con la descripcin de 'Primitive Operations' en [Libro, Seccin 4.2.2] y sealar las diferencias notables. Tendra sentido afirmar que la igualdad (==) de expresiones tiene complejidad constante? Cmo se implementa la igualdad de objetos en Java? * La invocacin de mtodos no tiene complejidad constante, depende de la complejidad del mtodo [Libro, Seccin 4.2.3, p170]: "except for method calls, of course". * Preguntarse si el anlisis de algoritmos descritos en pseudocdigo es realmente un anlisis terico de alto nivel. Cul es la semntica del pseudocdigo? No est el pseudocdatado a un paradigma de programacin o modelo de computacin [Transparencia 8]? Se hacen suposiciones realistas sobre el coste de la recursin? Se ignora el potencial para el paralelismo? 5 Arboles generales y binarios * 7 Tree Structures * Material * Libro: Captulo 7. * Transparencias: trees.pdf. * Indicamos el cdigo en las subsecciones siguientes. * 7.1 General Trees * General Trees: Terminologa y Definiciones * Motivacin: organizar informacin de forma jerrquica. * Utilizados en la implementacin de otros TADs. * La JCF no incluye una implementacin de rboles generales, se utilizan rboles en implementaciones de otros TADs (por ejemplo, Maps). * Definicin recursiva [Libro, p281, ltimas tres lneas]: Un rbol general es o bien vaco o bien un nodo raz que contiene un elemento y un conjunto de cero o ms (sub)rboles hijos. (Ntese que en un conjunto no hay orden ni repeticin). * Definicin ms formal [CLRS'01, B.5], [AHU'83, p231]: Un rbol libre ('free tree') es un grafo conectado, no-dirigido y acclico. Un rbol ordinario ('rooted tree') es un rbol libre en el que se elige un nodo como raz (se orientan las aristas al representarlo grficamente). * Terminologa [Transparencia 3]: * Raz ('root'): nodo sin padre. * Nodo interno ('internal node'): nodo con al menos un hijo. * Nodo externo ('external node'): nodo sin hijos. * Subrbol ('subtree'): nodo considerado como raz y todos sus descendientes. ERRORES: La descripcin de ancestro y de descendiente en [Transparencia 3] es incompleta. * Terminologa que no est o que completa [Transparencia 3] y que est en el libro o en otras fuentes: * Ancestro de un nodo v: un nodo w es ancenstro de v si v=w o w es ancestro del padre de v [Libro, p282]. * Descendiente de un nodo w (la inversa de ancestro): v es descendiente de w si w es ancestro de v [Libro, p282]. * Nodo hoja: nodo externo. Usaremos estos dos nombres indistintamente. * Hermano ('sibling') de un nodo: nodo con el mismo padre. * Arista ('edge') de un rbol: par de nodos en relacin padre-hijo o hijo-padre. * Grado ('degree') de un nodo: el nmero de hijos del nodo. Se puede extender sta definicin a todo el rbol: el grado de un rbol es el mximo de los grados de todos sus nodos. * Camino ('path') de un rbol: secuencia de nodos tal que cada nodo consecutivo forma una arista. La longitud del camino es el nmero de aristas. * rbol ordenado ('ordered tree'): existe un orden lineal (total) definido para los hijos de cada nodo: primer hijo, segundo hijo, etc. Se visualiza dibujando los hijos en orden de izquierda a derecha bajo el padre. Ejemplo: captulos de un libro. * Profundidad y altura de un nodo [Libro, '7.2.1 Depth and Height']: * La profundidad de un nodo ('depth') es la longitud del camino desde ese nodo a la raz (o viceversa). La longitud del camino es cero si el nodo es la raz. (El libro define la profundidad de un nodo de forma equivalente como el nmero de ancestros "propios" del nodo.) * La altura de un nodo ('height') es la longitud del mayor de todos los caminos del nodo a las hojas. * Daremos definiciones de profundidad y altura mediante mtodos de Java en *7.2 Tree Traversal Algorithms. * Altura de un rbol no vaco: la altura de la raz. * Profundidad y altura se han definido como propiedades de nodos. Por tanto, segn estas definiciones no tiene sentido hablar de la profundidad o la altura de un rbol vaco (sin nodos). * La profundidad de un rbol podra definirse como la mayor de las profundidades de las hojas, pero ese valor es igual a la altura. De hecho, la altura de un rbol tambin se define como el mximo de la profundidad de todos sus nodos [CLR'90, p94]. * Nivel ('level'): conjunto de nodos con la misma profundidad. As, tenemos desde el nivel 0 hasta el nivel 'h' donde 'h' es la altura del rbol. * Interfaz Tree * Cdigo: Tree.java, InvalidPositionException, EmptyTreeException.java, BoundaryViolationException.java. * Interfaz de rboles generales. El libro no muestra ni explica las interfaces y clases utilizadas en la implementacin del *Interfaz Tree. Se centra ms en implementaciones de rboles binarios y derivados. Tampoco ofrece cdigo para TADs que utilizaran el interfaz de rboles generales, por ejemplo, *10.4 (2,4) Trees. * Estudiamos este interfaz porque es la base del *Interfaz BinaryTree de rboles binarios que son los que veremos en ms profundidad. No veremos implementaciones de rboles generales en la asignatura. * El *Interfaz Position que se utilizaba en *6.2 Node Lists para abstraer la implementacin de los nodos de una lista se usa en 'Tree' para abstraer la implementacin de los nodos de un rbol. De nuevo, una posicin se entiende de forma relativa en funcin de las posiciones vecinas (que sern los padres y los hijos). * En el libro, el cdigo y las transparencias se usa la palabra nodo para referirse a una "posicin" ('Position') no a la clase que la implemente. Nosotros tambin seguimos esta convencin, si bien quedar claro por el contexto si por "nodo" nos referimos a "nodos abstractos" (interfaz 'Position') o a "nodos concretos" (alguna implementacin de dicho interfaz). * 'Tree' es un interfaz pensado para trabajar directamente con posiciones (abstracciones de nodos). Los TADs de rboles suelen usarse en implementaciones de otros TADs y para ello se necesita poder trabajar directamente (y eficientemente) con nodos. Esto explica que no es un interfaz recursivo, cuando s lo es la definicin de rbol general. Los mtodos trabajan con posiciones y no con rboles. Por ejemplo, se define: public Iterable children(Position v) throws ...y no: public Iterable children(Tree t) throws ...Por esta razn, los mtodos de las clases que usen el interfaz tomarn siempre un objeto rbol como argumento para poder invocarle los mtodos del interfaz. Ejemplos en *7.2 Tree Traversal Algorithms. * El interfaz importa 'java.util.Iterator' y define el mtodo 'iterator' directamente, no extiende 'Iterable'. Esto significa que un 'Tree' no es un 'Iterable'. * El mtodo 'iterator' devuelve un iterador para iterar sobre todos los nodos del rbol. En *7.2 Tree Traversal Algorithms veremos varias formas de recorrer los nodos. * Hay dos mtodos que devuelven 'Iterable': 1. Mtodo 'positions', que es lo que hemos llamado 'elements' en *Cmo iterar sobre TADs. 2. Mtodo 'children', que devuelve un iterable con los hijos del nodo. Si el rbol est ordenado el iterable estar ordenado segn el orden de los hijos. * Algunos mtodos lanzan excepciones cuando los nodos no son vlidos: * 'BoundaryViolationException' ser lanzada por 'parent' cuando el nodo argumento sea la raz. * 'EmptyTreeException' ser lanzada por 'root' cuando se invoque dicho mtodo sobre un objeto rbol vaco. * 'InvalidPositionException' lanzada por mtodos que toman posiciones como argumento cuando la posicin es invlida (p.ej., no es un objeto de una clase que implementa nodos de un rbol binario). * IMPORTANTE: El interfaz slo consta de mtodos observadores excepto un nico mtodo modificador 'replace' que permite modificar el elemento almacenado en un nodo. Esta decisin de diseo es deliberada [Libro, p284, ltimas lneas]: "we prefer to describe different tree update methods in conjunction with specific applications of trees in subsequent chapters. In fact, we can imagine several kinds of tree update operations beyond those given in this book". Los mtodos modificadores se definirn en las clases que implementen aquellos interfaces, como *Interfaz BinaryTree, que extienden 'Tree'. * 7.2 Tree Traversal Algorithms * Material * [Transparencias 5-6, trees.pdf] * Cdigo: Ejemplos que no son parte del paquete net.datastructures: http://higheredbcs.wiley.com/legacy/college/goodrich/0470383267/student/ch07/ch07-fragments.html En particular: * Trees-depth * Trees-height1 * Trees-height2 * Trees-preorderPrint * Trees-parentheticRepresentation * Trees-postorderPrint * Trees-diskSpace * Profundidad de un nodo * public static int depth(Tree T, Position v) {* if (T.isRoot(v))* return 0;* else* return 1 + depth(T, T.parent(v));* }* Explicamos por qu "static" y "" en *Traversals: comentarios y ejercicio. * Altura de un nodo (adaptado de Trees-height2) * public static int height(Tree T, Position v) {* int h = 0;* if (T.isExternal(v)) return h;* else* for (Position w : T.children(v))* h = Math.max(h, height(T, w));* return 1 + h;* }* 7.2.2 Preorder Traversal * [Transparencia 5] * Cdigo: Trees-preorderPrint, Trees-parentheticRepresentation. * public static String toStringPreorder(Tree T, Position v) {* String s = v.element().toString(); // the main "visit" action* for (Position w : T.children(v))* s += ", " + toStringPreorder(T, w);* return s;* }* 7.2.3 Postorder Traversal * [Transparencia 6] * Cdigo: Trees-postorderPrint, Trees-diskSpace. * public static String toStringPostorder(Tree T, Position v) {* String s = "";* for (Position w : T.children(v))* s += toStringPostorder(T, w) + " ";* s += v.element(); // main "visit" action* return s;* }* Traversals: comentarios y ejercicio * Los mtodos que implementan los recorridos toman como parmetros el rbol y un nodo. Hemos explicado las razones en *Interfaz Tree. * Los recorridos se implementan mediante mtodos genricos y estticos. Podemos asumir que son mtodos de alguna clase esttica no genrica, por ejemplo: * public static class Traversals {* public static int depth ...* public static int height ...* public static String toStringPreorder ...* public static String toStringPostorder ...* }* EJERCICIO: Definir dicha clase. * EJERCICIO: Se podra implementar dicha clase de forma que el rbol que se est recorriendo se almacene en un atributo y no tenga que pasarse como argumento a cada mtodo? En caso positivo, realizar los cambios necesarios en la clase para conseguirlo. * 7.3 Binary Trees * Material * [Trasparencias 7-15, 17-20, trees.pdf] * Cdigo: * Interfaces: BTPosition.java, BinaryTree.java. * Clases: BTNode.java, LinkedBinaryTree.java, EmptyTreeException.java NonEmptyTreeException.java InvalidPositionException.java, BoundaryViolationException.java. * Binary Trees: Terminologa y Definiciones * [Libro, p296-297] * Caso especial de rbol general en el que todo nodo tiene como mximo 2 hijos, el hijo izquierdo ('left child') y el hijo derecho ('right child'), que estn ordenados: el izquierdo precede al derecho. * Definicin recursiva: Un rbol binario es un rbol ordinario que es o bien vaco o bien un nodo raz con dos (sub)rboles binarios izquierdo y derecho. * El nodo hijo izquierdo/derecho es la raz del subrbol izquierdo/derecho. * Definicin de rbol binario propio ('proper binary tree'), tambin llamado rbol estrictamente binario ('strictly binary tree'): todo nodo interno tiene 2 hijos. Equivalentemente, todo nodo tiene o bien 0 o bien 2 hijos. Tambin llamado 2-Tree o rbol de grado 2. * o* / \* o o* / \* o o* Definicin de rbol binario impropio ('improper binary tree'): rbol binario que no es propio. * o* / \* o o* / \ \* o o o* Definicin de rbol binario perfecto ('perfect binary tree'): rbol binario propio con el mximo nmero de nodos. El nmero de nodos de un rbol binario (propio o impropio) es como mximo 2(h+1)-1. El nmero de nodos internos de un rbol binario (propio o impropio) es como mximo 2h-1. *7.3.3 Properties of Binary Trees. * o h = 0* 2^(h+1)-1 = 1* * o h = 1* / \ 2^(h+1)-1 = 3* o o* * o h = 2* / \ 2^(h+1)-1 = 7* o o* / \ / \* o oo oObsrvese que todas las hojas estn en el mismo nivel y todos los nodos internos tienen dos hijos. * [Libro, p296], [CLRS'01, B.5] definen rbol binario lleno ('full binary tree') como sinnimo de rbol propio. Otros ([AHU'83, p106], [Horowitz & Sahni "Data Structures in Pascal"]) lo definen como sinnimo de rbol binario perfecto. * Definicin de rbol binario equilibrado ('balanced binary tree'): para todo nodo, el valor absoluto de la diferencia de altura entre los dos subrboles hijos es como mximo 1. Ms formalmente: para todo nodo 'n' con hijo izquierdo 'i' e hijo derecho 'd' se tiene |h(i)-h(d)| Interfaz Position* ^ / ^* | / | E* | / |* | I /---- U ----> Interfaz BTPosition* | / ^* | / | I* | / |* Clase LinkedBinaryTree ----- U ----> Clase BTNode* * U: Usa. I: Implementa. E: Extiende. * Omitimos las clases de excepciones. * Cuestiones de implementacin de rboles binarios * Representacin de nodos: [Transparencia 17, trees.pdf] [Libro, p301, '7.3.4 A Linked Structure for Binary Trees']. * Un nodo tiene referencias al padre, al elemento, al hijo izquierdo y al hijo derecho. * En general se sigue la siguiente convencin en las figuras de rboles del libro y las transparencias: se dibujan los nodos internos como crculos y los externos como cuadrados. * Interfaz BinaryTree * Cdigo: BinaryTree.java. * Extiende 'Tree' con mtodos "getters" e interrogadores para nodos hijos que pueden lanzar excepciones. Los mtodos 'left' y 'right' lanzan 'BoundaryViolationException' si el nodo 'v' no tiene, respectivamente, hijo izquierdo o hijo derecho. Los mtodos lanzan 'InvalidPositionException' si el nodo 'v' no es vlido. * Igual que en *Interfaz Tree, no se definen mtodos modificadores [Libro, p298]: "we will consider some possible update methods when we describe specific implementations and applications of binary trees." Los mtodos modificadores se definirn en las clases que implementan el interfaz. * Interfaz BTPosition * Cdigo: BTPosition.java. * Recordamos que el *Interfaz Position slo ofrece el mtodo 'element' que devuelve el elemento almacenado en el nodo. * 'BTPosition' extiende 'Position' ofreciendo mtodos "setters" y "getters" para el resto de informacin que se almacenar en los nodos como atributos: referencias al nodo padre, al nodo hijo izquierdo y al nodo hijo derecho. Se aade un mtodo "setter" 'setElement' para modificar el elemento almacenado en el nodo. El mtodo 'element' no hace falta declararlo porque se hereda de 'Position' * Obsrvese la diferencia entre el mtodo 'left', que devuelve el nodo hijo izquierdo del objeto rbol sobre el que se invoca, y el mtodo 'getLeft' que devuelve el nodo hijo izquierdo del objeto nodo sobre el que se invoca. El primero lanza una excepcin si el nodo del rbol que toma como argumento no tiene hijo izquierdo mientras que el segundo no lanza ninguna excepcin, y por tanto podemos asumir que devolver 'null'. Dado un objeto 'tree' de una clase que implemente 'BinaryTree' y un objeto 'node' de una clase que implemente 'BTPosition' podremos asumir que 'tree.left(p).equals(p.getLeft())' si 'p' es un nodo vlido que tiene hijo izquierdo. Igualmente con 'right' y 'getRight'. * Obsrvese que los mtodos observadores devolvern objetos de tipo (de clases que implementen) 'BTPosition'. No devuelven objetos de tipo 'Position'. No tiene sentido que devuelvan 'Position', como tampoco que devolvieran 'Object'. La comprensin de este punto demuestra el conocimiento sobre el concepto de herencia que tenga el alumno. * Clase BTNode * Representacin de nodos: [Transparencia 17, trees.pdf] [Libro, p301]. * Cdigo: BTNode.java. * Implementa 'BTPosition'. * Cuatro atributos: referencia al nodo padre, referencia al elemento, referencia al hijo izquierdo y referencia al hijo derecho. * Dos constructores, uno que crea un nodo vaco (en el que todos los atributos quedan a null) y otro que construye un nodo dados valores para los atributos. * Se implementa el mtodo 'element' del *Interfaz Position. * Clase LinkedBinaryTree * Cdigo: LinkedBinaryTree.java. * Implementa el *Interfaz BinaryTree. * Ignoramos de momento los comentarios iniciales del cdigo que explican por qu en esta clase se usa directamente el atributo 'size' en vez de invocar el mtodo 'size' para obtener el nmero de nodos del rbol. (Se trata de un caso cuando menos cuestionable de una clase padre cuya implementacin est determinada por clases hijas. Volveremos sobre este punto en *10.1 Binary Search Trees.) * Se definen slo dos atributos: el tamao 'size' y el nodo raz 'root'. * El constructor crea un rbol vaco: 'root' a null y 'size' a 0. * El mtodo protegido 'checkPosition' [Cdigo, lnea 283] es utilizado por varios mtodos para comprobar la validez de un nodo (de un objeto que implementa el *Interfaz Position). Un nodo es vlido si no es null y es un objeto que implementa el *Interfaz BTPosition. Si el nodo es vlido se devuelve con casting a 'BTPosition'. (Por qu no a 'BTPosition'? Podra hacerse el casting a 'BTPosition?) Los mtodos que invocan 'checkPosition' utilizan una variable temporal ('vv', 'ww') de tipo 'BTPosition' para almacenar el nodo porque el tipo esttico ha sido cambiado por el casting. * Los mtodos 'isRoot', 'isInternal' e 'isExternal' implementan respectivamente las definiciones de riz, nodo interno y nodo externo que hemos visto en *General Trees: Terminologa y Definiciones. * Los mtodos 'hasLeft', 'hasRight', 'left', 'right' y 'parent' descansan en los "getters" del nodo, como anticipamos en *Interfaz BTPosition. El mtodo 'root' consulta el valor del atributo 'root' y el mtodo 'isRoot' descansa en el mtodo 'root'. * Los mtodos 'iterator', 'positions' y 'children' devuelven como iterable un una lista de posiciones *Clase NodePositionList. La lista es vaca si el rbol es vaco. La iteracin es un recorrido en preorden implementada por el mtodo protegido 'preorderPositions' [Cdigo, lnea 295]. El mtodo 'positions' devuelve la lista de nodos en preorden y el mtodo 'iterator' recorre la lista de nodos devuelta por 'positions' y crea una nueva lista con los elementos almacenados en los nodos. El mtodo 'children' devuelve una lista con los nodos hijos del nodo que toma como parmetro. Si dicho nodo no tiene hijos la lista es vaca. * Se definen mtodos modificadores que no estn en el *Interfaz BinaryTree: * Descritos en [Libro, p303]: 'addRoot', 'remove', 'attach', 'insertLeft' e 'insertRight'. Importante: Estos mtodos debemos estudiarlos pues son los que nos permiten, junto con el constructor de la clase, el poder crear rboles binarios. Ejemplos en *Construccin de rboles binarios. Recordamos que el *Interfaz BinaryTree extiende el *Interfaz Tree el cual slo declara un mtodo modificador: 'replace'. * No descritos en el libro: 'swapElements', 'expandExternal', 'removeAboveExternal', 'sibling', etc. Estos mtodos no necesitamos estudiarlos, pero es interesante entender su cdigo. Estos mtodos se ofrecen porque los usarn clases que extienden 'LinkedBinaryTree', como por ejemplo, *10.1 Binary Search Trees. * Se define un mtodo 'createNode' [Cdigo, lnea 290] que devuelve un objeto creado por el constructor de 'BTNode'. El mtodo 'createNode' es invocado por los mtodos modificadores 'addRoot', 'insertLeft' e 'insertRight'. Puede observarse que la construccin de nodos (objetos de clase 'BTNode') en la clase 'LinkedBinaryTree' solamente se hace invocando 'createNode'. Esto se hace as porque en el libro se definen clases que extienden 'LinkedBinaryTree' y sobrescriben ('override') 'createNode'. En Java los constructores de clase no pueden sobrescribirse. Por ello se usa un mtodo ordinario, que puede sobrescribirse, para construir nodos. * Se definen los mtodos 'preorderPositions' e 'inorderPositions' que aaden los nodos del rbol, recorridos en preorden y en inorden respectivamente, al final de una lista de posiciones (nodos) que toman como argumento. * Construccin de rboles binarios * La construccin de un rbol binario se puede hacer de varias maneras despus de llamar al constructor mediante combinaciones de 'addRoot', 'insertLeft', 'insertRight', 'attach', 'expandExternal', etc. * 1* / \* 2 3* / \ \* 4 5 6Construccin 1: LinkedBinaryTree tree = new LinkedBinaryTree();tree.insertLeft(tree.insertLeft(tree.addRoot(1),2),4);tree.insertRight(tree.left(tree.root()),5);tree.insertRight(tree.insertRight(tree.root(),3),6);Construccin 2: LinkedBinaryTree tree = new LinkedBinaryTree();tree.addRoot(1);tree.expandExternal(tree.root(),2,3);tree.expandExternal(tree.left(tree.root()),4,5);tree.insertRight(tree.right(tree.root()),6);* En ambos casos el objeto 'tree' es de clase 'LinkedBinaryTree' y no de tipo 'BinaryTree' porque ste ltimo interfaz no ofrece los mtodos modificadores que se invocan sobre el objeto 'tree'. Recordatorio de Java: * class Padre {}* class Hija extends Padre {* public void m() {}* }* class Main {* public void test() {* Padre p = new Hija();* p.m(); /* Error de compilacin: "cannot find symbol" */* }* }* class Padre {* public void m() {}* }* class Hija extends Padre {* public void m() {}* }* class Main {* public void test() {* Padre p = new Hija();* p.m(); /* Enlazado dinmico: se invoca el mtodo 'm'* * de la clase 'Hija'. */* }* }* 7.3.6 Traversals of Binary Trees * En preorden y en postorden. Se implementan usando los mtodos 'root', 'left' y 'right', que devuelven respectivamente los nodos raz y los nodos hijo izquierdo y derecho. * Inorden [Transparencia 12] [Cdigo, LinkedBinaryTree.java, lnea 306]. * EJERCICIO: Implementar 'postorderPositions' (que falta en el cdigo de LinkedBinaryTree.java). * Ejemplos de uso: mostrar y evaluar expresiones aritmticas. [Transparencias 13-14], [Libro, p312-316, junto con otros ejemplos]. * Recorrido Euler: [Transparencia 15 y 19-20], [Libro, p317-322]. No lo veremos en clase. * Complejidad de los mtodos * [Libro, pgina 309] * Para la implementacin LinkedBinaryTree.java. Mtodo Complejidad size, isEmpty O(1) iterator, positions O(n) replace O(1) root, parent, children, left, right, sibling O(1) hasLeft, hasRight, isInternal, isExternal, isRoot O(1) insertLeft, insertRight, attach, remove O(1) * La complejidad constante se consigue porque los parmetros y valores devueltos por los mtodos son nodos del rbol. 6 Colas con prioridad, montculos y ordenacin * 8 Priority Queues * Material * Libro: Captulo 8. * Transparencias: priorityqueues.pdf. * Cdigo Libro: * Interfaces: Entry.java, PriorityQueue.java. * Clases: DefaultComparator.java, EmptyPriorityQueueException.java, InvalidKeyException.java, SortedListPriorityQueue.java. * Cdigo Java Platform : * Interfaces: java.util.Comparator y java.lang.Comparable. * Motivacin de las colas con prioridad * En los TADs que hemos visto hasta ahora el lugar que ocupa un elemento en la implementacin del TAD es independiente de cualquier propiedad del elemento. * En las colas con prioridad la posicin que ocupa un elemento dentro de la cola viene determinada por la prioridad del elemento. Ejemplos: * Cola del supermercado en la que tiene preferencia algn colectivo. * Llegadas a urgencias en un hospital. * La prioridad de un elemento viene dada por un valor diferente del propio elemento que llamaremos clave ('key'). En adelante llamaremos al elemento valor ('value'). La asociacin de una clave a un valor, o par "clave-valor", lo llamaremos entrada ('entry'). * Convencin: la clave establece la prioridad inversamente, cuanto menor sea la clave mayor la prioridad. * Las colas con prioridad son TADs que almacenan entradas y ofrecen un mtodo que devuelve el valor (elemento) de menor clave (mayor prioridad). La complejidad de ste mtodo debe ser la mejor posible. En Java, utilizaremos tipos (interfaces, clases, etc) para claves y valores, que sern parmetros del interfaz de colas con prioridad. * Las claves y valores van por separado. La asignacin de claves a valores depende del problema. En ocasiones puede usarse como clave un atributo del valor (p.ej., el precio de un libro), pero debe tenerse un interfaz o clase aparte para poder ser usado el tipo clave como parmetro genrico: * public class Precio { ... }* public class Libro {* Precio precio;* String editorial;* ...* }* public class Main {* PriorityQueue cola = new ... ;* ...* }* [Libro, p334] Relaciones entre claves (prioridades) y valores (elementos), usamos la letra 'k' para claves y la letra 'v' para valores: * Dos entradas '(k,v1)' y '(k,v2)' con valores distintos pueden tener la misma clave: elementos distintos pueden tener la misma prioridad. * Dos entradas '(k1,v)' y '(k2,v)' con los mismos valores pueden tener claves distintas: el mismo elemento puede tener distintas prioridades. * Puede modificarse la clave o el valor de una entrada. La prioridad de un elemento puede cambiar. * 8.1 The Priority Queue Abstract Data Type * 8.1.1 Keys, Priorities, and Total Order Relations * En esta seccin estudiamos el concepto matemtico de orden total, tambin llamado orden lineal. * Desde la perspectiva de teora de conjuntos, una entrada es un par perteneciente al producto cartesiano de un conjunto 'K' de claves y un conjunto 'V' de valores: '(k,v)' est en 'K x V'. * Hay una relacin de orden total computable '