Partes de un Tipo Abstracto de Datos I Parte pública: Disponible para el usuario externo. I Introducción a la Computación (Matemática) I Primer Cuatrimestre de 2015 I Parte privada: Sólo accesible desde dentro del TAD. ¡El usuario externo nunca debe ver ni meterse en esto! I Tipos Abstractos de Datos Nombre y tipos paramétricos (ej: Fecha, Lista(ELEM)). Operaciones, sus especificaciones y posiblemente sus órdenes de complejidad temporal. I I Estructura de representación del TAD. Invariante de representación: qué propiedades debe cumplir la estructura elegida para que tenga sentido como la representación de una instancia del TAD. Algoritmos de las operaciones para la estructura de representación elegida. 1 2 TAD Fecha Operaciones públicas (para f , f1 , f2 : Fecha; d, m, a : Z): I CrearFecha(d, m, a) → Fecha I Dı́a(f ) → Z I Mes(f ) → Z I Año(f ) → Z I Menor(f1 , f2 ) → B I FechaSiguiente(f ) → Fecha Con esto, un usuario del TAD puede programar algo como: ContarDı́as(f1 , f2 ) → Z: RV ← 0 while (Menor(f1 , f2 )) { RV ← RV + 1 f1 ← FechaSiguiente(f1 ) } Olvidamos las listas de Python... Solamente tenemos B, Z, R y tuplas. (hasta el final de la clase de hoy!) 3 4 TAD Lista(ELEM) TAD Lista(ELEM) Operaciones (cont.): I Operaciones: I CrearLista() → Lista(ELEM): Crea una lista vacı́a. I L.Agregar(x): Inserta el elemento x al final de L. I L.Longitud() → Z: Devuelve la cantidad de elementos de L. I L.Iésimo(i) → ELEM: Devuelve el i-ésimo elemento de L. Precondición: 0 ≤ i < L.Longitud(). I L.Cantidad(x) → Z: Devuelve la cantidad de veces que aparece el elemento x en L. L.Insertar(i, x): Inserta el elemento x en la posición i de L, pasando los elementos de la posición i y siguientes a la posición inmediata superior. Pre: 0 ≤ i ≤ L.Longitud(). (Si L = a0 , .., an−1 , se convierte en L = a0 , .., ai−1 , x, ai , .., an−1 .) I L.BorrarIésimo(i): Borra el i-ésimo elemento de L Precondición: 0 ≤ i < L.Longitud(). (Si L = a0 , .., an−1 , se convierte en L = a0 , .., ai−1 , ai+1 , .., an−1 .) I donde L : Lista(ELEM); i : Z; x : ELEM (entero, char, etc.). L.Indice(x) → Z: Devuelve el ı́ndice (0 .. L.Longitud() − 1) de la primera aparición de x en L. Pre: L.Cantidad(x) > 0. donde L : Lista(ELEM), i : Z, x : ELEM (entero, char, etc.). ... y agregamos cualquier otra función que consideremos útil. 6 5 TAD Lista(ELEM) - Ejemplo de uso TAD Lista(ELEM) - Posible implementación Encabezado: Máximo : L ∈ Lista(Z) → Z Precondición: {L = L0 ∧ L.Longitud() > 0} Poscondición: {(∀i) 0 ≤ i < L0 .Longitud() ⇒ L0 .I ésimo(i) ≤ RV ∧ L0 .Cantidad(RV ) > 0} Vamos a representar una lista como una cadena de nodos. Definamos primero qué es un nodo. Nodo(ELEM) : hvalor : ELEM, siguiente : Ref(Nodo)i h...i define una tupla. m ← L.I ésimo(0) i ←1 while (i < L.Longitud()) { if (L.I ésimo(i) > m) { m ← L.I ésimo(i) } i ←i +1 } RV ← m Ref(Nodo) es una referencia a una instancia de tipo Nodo. Puede tomar el valor especial None (“referencia a nada”). Construcción de un nuevo Nodo: I n ← Nodo(x, r ) Acceso a los campos de un nodo n: 7 I n.valor devuelve el campo valor . I n.siguiente devuelve el campo siguiente. 8 TAD Lista(ELEM) - Posible implementación TAD Lista(ELEM) - Posible implementación Primero elegimos una estructura de representación del TAD Lista(ELEM) (que es privada). L.Agregar(x): nuevo ← Nodo(x, None) if (L.cabeza = None) { L.cabeza ← nuevo } else { aux ← L.cabeza while (aux.siguiente 6= None) { aux ← aux.siguiente } aux.siguiente ← nuevo } L.longitud ← L.longitud + 1 Lista(ELEM) == hcabeza : Ref(Nodo), longitud : Zi Después describimos el invariante de representación de esta estructura. En este caso longitud siempre debe ser igual a la cantidad de nodos encadenados a partir de cabeza, y en la cadena de nodos no deben formarse ciclos. Por último, damos los algoritmos de las operaciones del TAD Lista(ELEM) para la estructura de representación elegida: CrearLista() → Lista(ELEM): RV .cabeza ← None RV .longitud ← 0 10 9 TAD Lista(ELEM) - Posible implementación Partes de un Tipo Abstracto de Datos L.Longitud() → Z: RV ← L.longitud I Parte pública: Disponible para el usuario externo. I I L.Iésimo(i) → ELEM: aux ← L.cabeza while (i > 0) { aux ← aux.siguiente i ←i −1 } RV ← aux.valor (Pre: 0 ≤ i < L.Longitud()) I Parte privada: Sólo accesible desde dentro del TAD. ¡El usuario externo nunca debe ver ni meterse en esto! I I I Y ası́ con las otras operaciones del TAD Lista(ELEM): Cantidad, Insertar, BorrarIésimo e Indice. 11 Nombre y tipos paramétricos (ej: Fecha, Lista(ELEM)). Operaciones, sus especificaciones y posiblemente sus órdenes de complejidad temporal. Estructura de representación del TAD. Invariante de representación: qué propiedades debe cumplir la estructura elegida para que tenga sentido como la representación de una instancia del TAD. Algoritmos de las operaciones para la estructura de representación elegida. 12 TAD Lista(ELEM) - Posible implementación Complejidad temporal de los algoritmos vistos para esta estructura de representación. I CrearLista() → Lista(ELEM) I L.Agregar(x) I L.Longitud() → Z I L.Iésimo(i) → ELEM I L.Cantidad(x) → Z I L.Insertar(i, x) I L.BorrarIésimo(i) O(n) I L.Indice(x) → Z O(n) O(1) O(n) O(1) O(n) O(n) O(n) donde L : Lista(ELEM), i : Z, x : ELEM (entero, char, etc.). 14 13 TAD Pila(ELEM) Problema: Paréntesis balanceados Operaciones: I CrearPila() → Pila(ELEM): Crea una pila vacı́a. I P.Apilar(x): Inserta el elem. x sobre el tope de la pila P. I P.Vacı́a?(P) → B: Dice si la pila P está vacı́a. I P.Tope() → ELEM: Devuelve el elemento del tope de P. Precondición: ¬P.Vacı́a?(). I Dado un string, determinar si los caracteres { }, [ ], ( ) están balanceados correctamente. Ejemplos: P.Desapilar(): Borra el elemento del tope de P. Precondición: ¬P.Vacı́a?(). I “{a(b)x[()]}” está balanceado. I “}”, “a(b))” y “[[})” no están balanceados. ¿Se les ocurre una solución? Sugerencia: usar el tipo Pila. donde P : Pila(ELEM), x : ELEM (entero, char, etc.). Las pilas tienen estrategia LIFO (last in, first out). 15 16 TAD Pila(ELEM) - Posible implementación TAD Pila(ELEM) - Posible implementación Algoritmos de las operaciones del TAD Pila(ELEM) para la estructura de representación elegida: CrearPila() → Pila(ELEM): RV .ls ← CrearLista() Estructura de representación del TAD Pila(ELEM): Pila(ELEM) == hls : Listai P.Apilar(x): P.ls.Agregar(x) Invariante de representación de esta estructura: En este caso no hay nada que decir. Según la estructura de representación elegida, cualquier lista es una representación válida de una pila. P.Vacı́a?() → B: RV ← (P.ls.Longitud() = 0) P.Tope() → ELEM: (Pre: ¬P.Vacı́a?()) RV ← P.ls.Iésimo(P.ls.Longitud() − 1) P.Desapilar(): (Pre: ¬P.Vacı́a?()) P.ls.BorrarIésimo(P.ls.Longitud() − 1) 17 18 19 20 TAD Pila(ELEM) - Posible implementación La complejidad temporal de los algoritmos vistos para esta estructura de representación dependen de la complejidad de las operaciones del TAD Lista. Suponiendo la implementación del TAD Lista que vimos, los órdenes son: I CrearPila() → Pila(ELEM) I P.Apilar(x) I P.Vacı́a?() → B I P.Tope() → ELEM I P.Desapilar() O(1) O(n) O(1) O(n) O(n) ¿Se les ocurre otra estructura de representación que nos permita bajar todos estos a O(1)? TAD Cola(ELEM) Repaso de la clase de hoy Operaciones: I CrearCola() → Cola(ELEM): Crea una cola vacı́a. I TAD Lista(ELEM). I C .Encolar(x): Agrega el elemento x al final de la cola C . I TAD Pila(ELEM). I C .Vacı́a?() → B: Dice si la cola C está vacı́a. I TAD Cola(ELEM). I C .Primero() → ELEM: Devuelve el primer elemento de C . Precondición: ¬C .Vacı́a?(). I C .Desencolar(): Borra el primer elemento de C . Precondición: ¬C .Vacı́a?(). Próximos temas I I donde C : Cola(ELEM), x : ELEM (entero, char, etc.). Conjunto(ELEM) y Diccionario(T1 , T2 ). Técnicas algorı́tmicas: I Backtracking. 8 reinas. Las colas tienen estrategia FIFO (first in, first out). 21 22