TAD Lineales: Pila, Cola y Lista Objetivos Conocer los TAD lineales, su funcionamiento y aplicaciones Conocer la especificación de cada uno de ellos Saber realizar implementaciones alternativas Saber usar los tipos lineales para resolver problemas Conocer y utilizar los contenedores equivalentes de la STL Contenidos 2.1 Introducción a las estructuras lineales 2.2 El TAD Pila 2.2.1 Conceptos 2.2.2 Especificación algebraica 2.2.3 Implementación 2.2.4 Consideraciones sobre TAD basados en punteros 2.2.5 El contenedor stack de la STL Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 1
TAD Lineales: Pila, Cola y Lista Contenidos 2.3 El TAD Cola 2.3.1 Conceptos 2.3.2 Especificación algebraica 2.3.3 Implementación 2.3.4 El contenedor queue de la STL 2.3.5 El TAD Doble cola 2.4 El TAD Lista 2.4.1 Conceptos 2.4.2 Lista con índices 2.4.3 Lista con iteradores 2.4.4 Los contenedores vector y list de la STL Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 2
TAD Lineales: Pila, Cola y Lista Duración 5 clases (7.5 h) Bibliografía Data Structures and Algorithms in C++ Autor: Michael T. Goodrich, Roberto Tamassia, David M. Mount Editorial: John Wiley & Sons, Inc. Año: 2004 Págs. 143 252 Estructuras de datos y métodos algorítmicos Autor: Narciso Martí Oliet y otros Editorial : Prentice-Hall, 2004 Págs. 39 146 Diseño de programas. Formalismo y abstracción Autor: Ricardo Peña Marí Editorial : Prentice-Hall, 1999 Págs. 227 234 Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 3
TAD Lineales: Pila, Cola y Lista 2.1 Introducción a las estructuras lineales Estructuras lineales importantes porque aparecen con frecuencia en muchos problemas Ejemplos: una cola de clientes de un banco, la pila de ejecución de un programa, los caracteres de una cadena o las páginas de un libro Características: existe un elemento llamado primero existe un elemento llamado último cada elemento, excepto el primero, tiene un único predecesor cada elemento, excepto el último, tiene un único sucesor Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 4
TAD Lineales: Pila, Cola y Lista Operaciones básicas: crear la estructura vacía insertar un elemento borrar un elemento obtener un elemento Para definir claramente el comportamiento de la estructura es necesario determinar en qué posición se inserta un elemento nuevo y qué elemento se borra o se obtiene Principales estructuras lineales: pilas, colas y listas Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 5
TAD Pila 2.2 El TAD Pila 2.2.1 Conceptos Una pila es un contenedor de objetos que son insertados y eliminados de acuerdo con el principio de que el último en entrar es el primero en salir (LIFO, Last Input First Output) Los elementos se insertan de uno en uno (apilar) Se sacan en el orden inverso al cual se han insertado (desapilar) El único elemento que se puede observar dentro de la pila es el último insertado (cima) Apilar a 3 a 2 a 1 Desapilar Cima de la pila Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 6
TAD Pila Aplicaciones: estructuras auxiliares en numerosos algoritmos y esquemas de programación: recorridos de árboles y grafos evaluación de expresiones conversión entre notaciones de expresiones (postfija, prefija, infija) gestión de los registros de activación de los subprogramas activos durante la ejecución de un programa los editores de texto proporcionan normalmente un botón deshacer que cancela las operaciones de edición recientes y restablece el estado anterior del documento. La secuencia de operaciones recientes se mantiene en una pila los navegadores permiten habitualmente volver hacia atrás en la secuencia de páginas visitadas. Las direcciones de los sitios visitados se almacenan en una pila Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 7
TAD Pila 2.2.2 Especificación algebraica espec pilas usa booleanos parámetro formal género elemento fpf género pila operaciones creapila: pila apilar: pila elemento pila parcial desapilar: pila pila parcial cima: pila elemento vacía?: pila booleano dominios de definición p: pila; e: elemento ecuaciones p: pila; e: elemento desapilar (apilar (p, e)) = cima (apilar (p, e)) = vacia? (creapila) = vacia? (apilar (p, e)) = fespec Cons (pila) = Obs (pila) = Gen (pila) = Mod (pila) = Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 8
TAD Pila El conjunto de generadoras es libre, ya que cualquier término formado por las operaciones generadoras denota siempre un valor distinto del TAD Pila Los patrones necesarios para representar todas las posibles pilas se obtienen del conjunto de las operaciones generadoras creapila: representa la pila sin ningún elemento (pila vacía) apilar(p, e): representa cualquier pila con, al menos, un elemento Las operaciones desapilar y cima no están definidas para la pila vacía Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 9
TAD Pila 2.2.3 Implementación Interfaz informal Paradigma TAD: Es necesario especificar el tipo independientemente de su implementación Una interfaz es una lista de métodos que cualquier implementación del tipo debe proporcionar Aunque C++ no soporta de forma adecuada la definición de interfaces, para cada tipo presentaremos una interfaz informal template <typename T> Pila { public: Pila(); bool esvacia() const; const T& cima() const throw(pilavaciaexcepcion); void apilar (const T& objeto); void desapilar() throw(pilavaciaexcepcion); } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 10
TAD Pila Transformación de la notación algebraica a OO La operación constante creapila se convierte en el constructor de la clase El parámetro cuyo tipo es el que aparece en el género se convierte en el objeto sobre el que se aplica el método esvacía, cima y desapilar se transforman en métodos sin parámetros apilar se transforma en un método con un único parámetro Las operaciones parciales (cima y desapilar) se convierten en métodos que lanzan excepciones para el subconjunto del dominio para el que no están definidas Las operaciones observadoras (cima y vacía?) se implementan como métodos constantes Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 11
TAD Pila Mediante una tabla n indcima elementos e 1...... e 1 e 2 e 3 e n 0 1 2... n-1... capacidad - 1 template <typename T> class PilaTabla { public: PilaTabla(int tam = 10); PilaTabla(const PilaTabla& p); bool esvacia() const; const T& cima() const throw(pilavaciaexcepcion); void apilar (const T& objeto) throw(pilallenaexcepción); void desapilar throw(pilavaciaexcepcion); ~PilaTabla (); private: int capacidad; T* elementos; int indcima; } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 12
TAD Pila Consideraciones: si la pila no está vacía, el elemento almacenado en elementos[indcima - 1] corresponde a la cima de la pila elementos[0] fondo de la pila indcima = 0 pila vacía Ventaja: todas las operaciones de la interfaz tienen un coste temporal constante O(1) Inconvenientes: reservar espacio de memoria para el máximo previsto de elementos gestionar pila llena lanzar una excepción usar la clase vector de la STL Opciones para controlar la parcialidad o situaciones de error 1. controlar el posible error dentro de la operación 2. proporcionar suficientes operaciones para que el propio usuario evite el error (como sucede en la clase stack de la STL) Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 13
TAD Pila En C++, se puede hacer una reserva dinámica de la memoria para los arrays mediante el operador new Por esa razón, en la parte privada de la clase no se declara un tamaño para el array elementos Será el constructor el que reserve el espacio necesario en función del tamaño especificado como parámetro PilaTabla(int tam = 10) inicio indcima = 0; capacidad = tam; elementos = nuevo T[capacidad]; fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 14
TAD Pila Constructor de copia para la clase PilaTabla PilaTabla(const PilaTabla& p) var int i; fvar; inicio fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 15
TAD Pila Mediante una lista enlazada c e 1 e 2 e 3... template <typename T> class PilaEnlazada { typedef Nodo<T>* PtrNodo; public: PilaEnlazada(); PilaEnlazada(const PilaEnlazada& p); bool esvacia() const; const T& cima() throw(pilavaciaexcepcion) const; void apilar (const T& objeto); void desapilar throw(pilavaciaexcepcion); ~PilaEnlazada(); private: PtrNodo c; } Ventajas: todas las operaciones de la interfaz tienen un coste temporal constante O(1) no hay limitaciones de tamaño Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 16
TAD Pila Se define una clase Nodo, que representa a cada uno de los objetos que forman la lista enlazada template <typename T> class Nodo { typedef Nodo<T>* PtrNodo; public: Nodo(const T& objeto); Nodo(const T& objeto, PtrNodo psig); Nodo(const Nodo& n); const T& getobj() const; PtrNodo getsig() const; void setobj(const T& objeto); void setsig(ptrnodo psig); private: T obj; PtrNodo sig; } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 17
TAD Pila 2.2.4 Consideraciones sobre TAD basados en memoria dinámica Si se realiza una asignación entre dos variables de tipo puntero (ej., p = q), no se realiza una copia del valor del tipo (p.e. una pila completa), sino que se duplica la forma de acceder a ella p 4 6 8 q 1 3 5 p 4 6 8 q 1 3 5 Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 18
TAD Pila Constructor de copia para la clase PilaEnlazada PilaEnlazada(const PilaEnlazada& p) var PtrNodo pv, n, antn; fvar; inicio c = NULO; pv = p.c; si pv NULO entonces n = nuevo Nodo<T>(pv->getObj()); c = n; antn = n; pv = pv->getsig(); mientras pv NULO hacer n = nuevo Nodo<T>(pv->getObj()); antn->setsig(n); antn = n; pv = pv->getsig(); fmientras; fsi fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 19
TAD Pila Cuando dejamos de necesitar una estructura dinámica, debemos ocuparnos de liberar la memoria En caso contrario, esos objetos seguirán existiendo en memoria aunque no podamos acceder a ellos (es el caso de la pila formada por los elementos 4-6-8 de la figura anterior) Método destructor para la clase PilaEnlazada: ~PilaEnlazada() inicio mientras esvacia() hacer desapilar(); fmientras; fin; Método destructor para la clase PilaTabla: ~PilaTabla() inicio liberar [ ] elementos; fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 20
TAD Pila 2.2.4 El contenedor stack de la STL Uso: #include <stack> // stack<tipo_base> p; Operaciones básicas: stack() stack(const stack&) stack& operator=(const stack&) bool empty() const const tipo_base& top() const void push(const tipo_base&) void pop() constructor constructor de copia operador de asignación esvacia cima apilar desapilar Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 21
TAD Cola 2.3 El TAD Cola 2.3.1 Conceptos Los elementos se añaden por el extremo final, y se eliminan por el extremo opuesto: frente El único elemento observable en todo momento es el primero que fue insertado Se le suele denominar estructura FIFO (First Input First Output). Aplicaciones Colas de trabajos a realizar por una impresora Asignación de tiempo de procesador a los procesos en un sistema multiusuario (sin prioridad) Simular situaciones reales: cajero automático, llamadas en espera,... Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 22
TAD Cola 2.3.2 Especificación algebraica espec colas usa booleanos parámetro formal género elemento fpf género cola operaciones creacola: cola añadir: cola elemento cola parcial eliminar: cola cola parcial primero: cola elemento vacía?: cola booleano dominios de definición c: cola; e: elemento... Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 23
TAD Cola Las operaciones son: Gen (cola) = Mod (cola) = Obs (cola) = El conjunto de generadoras es libre? Los patrones necesarios para representar todas las posibles colas se obtienen del conjunto de las operaciones generadoras creacola: representa la cola sin ningún elemento (cola vacía) añadir(c, e): representa cualquier cola con, al menos, un elemento Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 24
TAD Cola ecuaciones c: cola; e: elemento eliminar (añadir (c, e)) = primero (añadir (c, e)) = vacia? (creacola) = vacia? (añadir (c, e)) = fespec Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 25
TAD Cola 2.3.3 Implementación Interfaz informal template <typename T> Cola { public: Cola(); bool esvacia() const; const T& primero() const throw(colavaciaexcepcion); void añadir(const T& objeto); void eliminar() throw(colavaciaexcepcion); } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 26
TAD Cola Cola primero último e 1 e 2 e n... Ventaja: todas las operaciones de la interfaz tienen un coste temporal constante O(1) template <typename T> class Cola { typedef Nodo<T>* PtrNodo; public: Cola(); Cola(const Cola& p); bool esvacia() const; const T& primero() const throw(colavaciaexcepcion); void añadir(const T& objeto); void eliminar() throw(colavaciaexcepcion); ~Cola(); private: PtrNodo primero, ultimo; } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 27
TAD Cola Cola() inicio primero = ultimo = NULO; fin; void añadir(const T& objeto) var PtrNodo n; fvar; inicio n = nuevo Nodo<T>(objeto); si esvacia() entonces primero = n; sino ultimo->setsig(n); fsi; ultimo = n; fin; void eliminar() throw(colavaciaexcepcion); var PtrNodo inicio; fvar; inicio si esvacia() entonces lanzar ColaVaciaExcepcion(); fsi; inicio = primero; si primero == ultimo entonces ultimo = NULO; fsi; primero = primero->getsig(); liberar inicio; fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 28
TAD Cola 2.3.4 El contenedor queue de la STL Uso: #include <queue> // queue<tipo_base> c; Operaciones básicas: queue() queue(const queue&) queue& operator=(const queue&) bool empty() const const tipo_base& front() const void push(const tipo_base&) void pop() constructor constructor de copia operador de asignación esvacia primero añadir eliminar Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 29
TAD Cola 2.3.5 El TAD Doble Cola Permite consultar, añadir y eliminar elementos en cualquiera de los dos extremos de la estructura lineal Recibe también el nombre de bicola Especificación algebraica espec dobles-colas usa booleanos, naturales parámetro formal género elemento fpf género doblecola operaciones Cons (doblecola) = Obs (doblecola) = Gen (doblecola) = Mod (doblecola) = creadc: doblecola +izq: elemento doblecola doblecola parcial -izq: doblecola doblecola parcial -dch: doblecola doblecola +dch: doblecola elemento doblecola parcial izq: doblecola elemento vacía?: doblecola booleano parcial dch: doblecola elemento... Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 30
TAD Cola dominios de definición e: elemento; c: doblecola -izq (+izq (e, c)) -dch (+izq (e, c)) izq (+izq (e, c)) dch (+izq (e, c)) ecuaciones e, e1, e2: elemento; c: doblecola +dch (creadc, e) = +dch (+izq (e1, c), e2) = -izq (+izq (e, c)) = -dch (+izq (e, c)) = esta ecuación es equivalente a las ecuaciones -dch (+izq (e, creadc)) = -dch (+izq ( e1, +izq (e2, c))) =... Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 31
TAD Cola izq (+izq (e, c)) = dch (+izq (e, c)) = esta ecuación es equivalente a las ecuaciones dch (+izq (e, creadc) = dch (+izq (e1, +izq (e2, c))) = vacía? (creadc) = vacía? (+izq (e, c)) = fespec Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 32
TAD Cola Implementación Interfaz informal template <typename T> DobleCola { public: DobleCola(); bool esvacia() const; const T& observarizq() const throw(doblecolavaciaexcepcion); const T& observarder() const throw(doblecolavaciaexcepcion); void añadirizq(const T& objeto); void añadirder(const T& objeto); void eliminarizq() throw(doblecolavaciaexcepcion); void eliminarder() throw(doblecolavaciaexcepcion); } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 33
TAD Cola Lista simplemente enlazada Doble cola primero último Problema: eliminar el elemento de la derecha tiene un coste temporal lineal O(n) e 1 e 2 e n... Ventajas: Lista doblemente enlazada Doble cola primero último todas las operaciones de la interfaz tiene un coste temporal constante O(1) Los nodos centinelas simplifican las operaciones de inserción y eliminación... e 1 e 2 e n Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 34
TAD Cola Se define una clase DobleNodo, que representa a cada uno de los objetos que forman la lista doblemente enlazada template <typename T> class DobleNodo { typedef DobleNodo<T>* PtrDobleNodo; public: DobleNodo(); DobleNodo(PtrDobleNodo pant, PtrDobleNodo psig); DobleNodo(const T& objeto, PtrDobleNodo pant, PtrDobleNodo psig); DobleNodo(const DobleNodo& n); const T& getobj() const; PtrDobleNodo getsig() const; PtrDobleNodo getant() const; void setobj(const T& objeto); void setsig(ptrdoblenodo psig); void setant(ptrdoblenodo pant); private: T obj; PtrDobleNodo sig; PtrDobleNodo ant; } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 35
TAD Cola La clase DobleCola template <typename T> class DobleCola { typedef DobleNodo<T>* PtrDobleNodo; public: DobleCola(); DobleCola(const Cola& p); bool esvacia() const; const T& observarizq() const throw(doblecolavaciaexcepcion); const T& observarder() const throw(doblecolavaciaexcepcion); void añadirizq(const T& objeto); void añadirder(const T& objeto); void eliminarizq() throw(doblecolavaciaexcepcion); void eliminarder() throw(doblecolavaciaexcepcion); ~DobleCola(); private: PtrDobleNodo primero, ultimo; } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 36
TAD Cola DobleCola() inicio primero = nuevo DobleNodo<T>(); último = nuevo DobleNodo<T>(primero, NULO); primero->setsig(ultimo); fin; void eliminarder() throw (DobleColaVaciaExcepcion) var PtrDobleNodo p; fvar; inicio si esvacia() entonces lanzar DobleColaVaciaExcepcion(); fsi; p = ultimo->getant(); p->getant()->setsig(ultimo); ultimo->setant(p->getant()); liberar p; fin; void añadirizq(const T& objeto) var PtrDobleNodo n; fvar; inicio n = nuevo DobleNodo<T>(objeto, primero, primero->getsig()); primero->setsig(n); n->getsig()->setant(n); fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 37
TAD Cola El contenedor deque de la STL Uso: #include <deque> // deque<tipo_base> dc; Operaciones básicas: deque() deque(const deque&) deque& operator=(const deque&) bool empty() const const tipo_base& front() const const tipo_base& back() const void push_front(const tipo_base&) void push_back(const tipo_base&) void pop_front() void pop_back() constructor constructor de copia operador de asignación esvacia observarizq observarder añadirizq añadirder eliminarizq eliminarder Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 38
TAD Lista 2.4 El TAD Lista 2.4.1 Conceptos Generalización de los TAD Pila, Cola y Doble Cola Secuencia de elementos en la que se permite el acceso para consultar, añadir o eliminar elementos en cualquier posición Suelen utilizarse como base para la construcción de otros TAD y estructuras de datos: matrices dispersas (listas de listas) árboles n-arios (listas de nodos "hermanos") grafos (listas de adyacencia) Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 39
TAD Lista Existen diversas formas de generar las listas. En nuestro caso elegimos un conjunto de operaciones pequeño y libre Nos apoyamos en la definición recursiva de secuencia de elementos: colección de elementos del mismo tipo que: bien es vacío, en cuyo caso se denomina secuencia vacía bien se obtiene tras añadir por la izquierda un elemento a otra secuencia existente Operaciones generadoras Gen (lista) = [ ] +izq (e, l) Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 40
TAD Lista 2.4.2 Lista con índices Existe una posición (índice) que sirve de referencia para las operaciones de inserción, consulta y modificación en cualquier posición Especificación algebraica espec listas usa booleanos, naturales parámetro formal género elemento operaciones _ == _: elemento elemento booleano _ _: elemento elemento booleano fpf género lista operaciones [ ]: lista parcial -izq: lista lista +izq: elemento lista lista parcial -dch: lista lista [ _ ] : elemento lista parcial izq: lista elemento _ & _ : lista lista lista parcial dch: lista elemento +dch: lista elemento lista longitud: lista natural vacía?: lista booleano está?: elemento lista booleano... Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 41
TAD Lista parcial insertar: lista nat elemento lista parcial eliminar: lista nat lista parcial modificar: lista nat elemento lista parcial _ [ _ ] : lista nat elemento parcial pos: elemento lista natural {insertar elemento i-ésimo} {eliminar elemento i-ésimo} {modificar elemento i-ésimo} {elemento i-ésimo} {posición del elemento} dominios de definición e: elemento; l: lista; i: natural -izq (+izq (e, l)) -dch (+izq (e, l)) izq (+izq (e, l)) dch (+izq (e, l)) insertar (l, i, e) está definido sólo si (1 i) (i long (l) + 1) eliminar (l, i) está definido sólo si (1 i) (i long (l)) modificar (l, i, e) está definido sólo si (1 i) (i long (l)) l[i] está definido sólo si (1 i) (i long (l)) pos (e, l) está definido sólo si está? (e, l)... Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 42
TAD Lista ecuaciones e, e1, e2: elemento; l: lista; i: natural [e] = [ ] & l = +izq (e, l1) & l2 = long ([ ]) = long (+izq (e, l)) = está? (e, [ ]) = está? (e1, +izq (e2, l)) = insertar ([ ], i, e) = insertar (+izq (e1, l), i, e2)) =... Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 43
TAD Lista eliminar (+izq (e, l), i) = modificar (+izq (e1, l), i, e2) = +izq (e, l) [i] = pos (e1, +izq (e2, l)) = fespec Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 44
TAD Lista Implementación Interfaz informal template <typename T> Lista { public: Lista(); bool esvacia() const; int longitud() const; void añadirizq(const T& objeto); void añadirdch(const T& objeto); void eliminarizq() throw(listavaciaexcepcion); void eliminardch() throw(listavaciaexcepcion); const T& observarizq() const throw(listavaciaexcepcion); const T& observardch() const throw(listavaciaexcepcion); void concatenar(const Lista& list); bool pertenece(const T& objeto) const; void insertar(int posicion, const T& objeto) throw(posicionerroneaexcepcion); void eliminar(int posicion) throw(posicionerroneaexcepcion); void modificar(int posicion, const T& objeto) throw(posicionerroneaexcepcion); const T& observar(int posicion) const throw(posicionerroneaexcepcion); int posicion(const T& objeto) const throw(objetoinexistenteexcepcion); ~Lista(); } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 45
TAD Lista Lista simplemente enlazada La implementación más sencilla del TAD Lista es mediante una lista simplemente enlazada: primero ultimo... e 1 e 2 e n template <typename T> class Lista { public: // operaciones del TAD private: PtrNodo primero, ultimo; int num; } n num Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 46
TAD Lista En la siguiente tabla se observa el coste de las operaciones, para el peor caso, utilizando este tipo de representación operación coste operación coste añadirizq longitud eliminarizq pertenece observarizq insertar esvacía eliminar concatenar modificar añadirdch observar observardch posición eliminardch n longitud de la lista Cómo se podría mejorar el coste de la operación eliminardch? Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 47
TAD Lista Lista() inicio primero = ultimo = NULO; num = 0; fin; void añadirizq(const T& objeto); var PtrNodo n; fvar; inicio n = nuevo Nodo(objeto, primero); primero = n; si num == 0 entonces ultimo = n; fsi; num++; fin; void eliminardch() throw(listavaciaexcepcion); var PtrNodo anterior; fvar; inicio si esvacia() entonces lanzar ListaVaciaExcepcion(); fsi si num == 1entonces liberar primero; primero = ultimo = NULO; si no anterior = primero; mientras anterior->getsig() ultimo hacer anterior = anterior->getsig(); fmientras anterior->setsig(nulo); liberar ultimo; ultimo = anterior; fsi num--; fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 48
TAD Lista 2.4.3 Lista con iteradores Un iterador es una generalización del concepto de puntero Un iterador asociado a una lista está formado por: una lista L una posición en L (posición actual) un conjunto de operaciones para desplazar la posición actual dentro de L Un iterador abstrae los aspectos de la representación interna de la lista y, en general, de cualquier contenedor, durante los desplazamientos y accesos para lectura o escritura proporciona una interfaz de uso independiente del contenedor La ganancia en eficiencia de los iteradores se logra cuando se pretende realizar accesos sucesivos a elementos consecutivos de una lista Los accesos aleatorios mediante iteradores pueden no ser posibles o ser tan ineficientes como los accesos mediante índices Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 49
TAD Lista Especificación algebraica de iterador espec iteradores usa listas, naturales parámetro formal género elemento fpf género iterador operaciones inicio: lista iterador parcial avanzar: lista iterador iterador parcial observar: lista iterador elemento parcial posición: lista iterador natural dominios de definición e: elemento; l: lista; it: iterador avanzar (l, it) está definido sólo si 1 posición(l, it) longitud (l) observar (+izq(e, l), it) 1 posición (+izq(e,l), it) longitud (+izq(e,l)) it es iterador de la lista que interviene en cada operación ecuaciones l: lista; it: iterador posición (l, inicio (l)) = 1 posición (l, avanzar (l, it)) = 1 + posición (l, it) observar (l, it) = l[posición (l, it)] fespec Gen (iterador) = Obs (iterador) = NOTA: los iteradores pueden posicionarse desde el primer elemento de la lista hasta la posición siguiente al último elemento (denominaremos a esta posición final de lista) Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 50
TAD Lista Enriquecimiento de la especificación algebraica de lista con las operaciones con iteradores usa..., iteradores... operaciones parcial insertar: lista iterador elemento lista {inserta un elemento delante del iterador } parcial eliminar: lista iterador lista parcial modificar: lista iterador elemento lista parcial observar: lista iterador elemento principio: lista iterador final: lista iterador parcial poneriterador: lista natural iterador {elimina el elemento posicionado en el iterador} {modifica el elemento posicionado en el iterador} {obtiene el elemento posicionado en el iterador} {sitúa el iterador en el primer elemento de la lista} {sitúa el iterador en la posición siguiente al último elemento} {sitúa el iterador en la posición indicada por el parámetro} dominios de definición e: elemento; l: lista; it: iterador; n: natural insertar (l, it, e) está definido sólo si (1 posición(l, it)) (posición(l, it) longitud (l) + 1) eliminar (l, it) está definido sólo si (1 posición(l, it) (posición(l, it) longitud (l)) modificar (l, it, e) está definido sólo si (1 posición(l, it)) (posición(l, it) longitud (l)) observar (l, it) está definido sólo si (1 posición(l, it)) (posición(l, it) longitud (l)) poneriterador (l, n) está definido sólo si (1 n) (n longitud (l) + 1) it es iterador de la lista que interviene en cada operación Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 51
TAD Lista Ecuaciones para las nuevas operaciones: ecuaciones e: elemento; l: lista; n: natural; it: iterador principio (l) = final (l) = poneriterador (l, n) = Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 52
TAD Lista La semántica del resto de las operaciones con iteradores (insertar, eliminar, modificar y observar) se describe en función de las operaciones análogas con índices. Simplemente se utiliza como índice el resultado de la operación posición (l, it) Aunque tengan el mismo nombre, al tener parámetros distintos, entendemos que estamos haciendo sobrecargas de operaciones ecuaciones e: elemento; l: lista; it: iterador insertar (l, it, e) = eliminar (l, it) = modificar (l, it, e) = observar (l, it) = fespec Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 53
TAD Lista Implementación Interfaz informal template <typename T> Lista { public: void insertar(const Iterador& it, const T& objeto); void eliminar(const Iterador& it) throw(posicionerroneaexcepcion); void modificar(const Iterador& it, const T& objeto) throw(posicionerroneaexcepcion); const T& observar(const Iterador& it) const throw(posicionerroneaexcepcion); const Iterador& principio() const; const Iterador& final() const; {sitúa el iterador en la siguiente posición al último elemento} const Iterador& poneriterador(int posicion) const throw(indiceerroneoexcepcion); } template <typename T> Iterador{ public: void avanzar(const Lista& L) throw(posicionerroneaexcepcion); const T& observar(const Lista& L) const throw(posicionerroneaexcepcion); int posicion(const Lista& L) const; } Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 54
TAD Lista Como todos los tipos vistos hasta hora, los iteradores tienen una interfaz común, independientemente del contenedor al que va a estar asociado Sin embargo, su representación y la implementación de sus métodos dependerá de la representación elegida para el contenedor (en este caso para la lista) Con una implementación de lista simplemente enlazada, el iterador consiste en un puntero al nodo anterior para que las operaciones de inserción y borrado sean eficientes. Además, la lista debe tener un nodo centinela al principio para poder tratar al primer elemento Lista num primero último e 1... e n anterior Iterador En este gráfico vemos un iterador apuntando al elemento e 1. Este hecho representa, conceptualmente, al iterador posicionado en el elemento e 2 Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 55
TAD Lista template <typename T> class Lista { typedef Nodo<T>* PtrNodo; public: class Iterador { friend class Lista<T>; public: } void avanzar(const Lista& L) throw(posicionerroneaexcepcion); const T& observar(const Lista& L) const throw(posicionerroneaexcepcion); int posicion(const Lista& L) const; private: PtrNodo anterior; Iterador (PtrNodo i); }; void insertar(const Iterador& it, const T& objeto); void eliminar(const Iterador& it) throw(posicionerroneaexcepcion); void modificar(const Iterador& it, const T& objeto) throw(posicionerroneaexcepcion); const T& observar(const Iterador& it) const throw(posicionerroneaexcepcion); const Iterador& principio() const; const Iterador& final() const; const Iterador& poneriterador(int posicion) const throw(indiceerroneoexcepcion); private: PtrNodo primero, ultimo; int num; hacemos a la clase Lista amiga de la clase iterador para que los métodos de la clase Lista puedan acceder a su parte privada el constructor de iterador es privado para que sólo lo utilicen los métodos de la clase Lista La clase Iterador es una clase anidada de la clase Lista. Con esto se consiguen iteradores propios para cada TAD Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 56
TAD Lista Lista() inicio primero = ultimo = nuevo Nodo(); num = 0; fin; void insertar(const T& objeto, Iterador& it); inicio it.anterior->setsig(nuevo Nodo(objeto, it.anterior->getsig())); si (it.anterior == ultimo) entonces ultimo = it.anterior -> getsig(); fsi it.anterior = it.anterior->getsig(); num++; fin; void eliminar(const Iterador& it) throw(posicionerroneaexcepcion); inicio si (it.anterior->getsig() == NULL) lanzar PosicionErroneaExcepcion(); sino PtrNodo p = it.anterior->getsig(); si p = ultimo entonces ultimo = it.anterior; fsi it.anterior->setsig(p->getsig()); delete p; num--; fsi fin; const Iterador& principio() inicio devuelve nuevo Iterador (primero); fin; const Iterador& final() inicio devuelve nuevo Iterador (ultimo); fin; Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 57
TAD Lista Hay que tener un especial cuidado con el uso de iteradores: se puede emplear un iterador definido para una lista en operaciones con otra lista Puede evitarse incluyendo un tratamiento de excepciones que compruebe que el iterador pertenece a la lista con la que se quiere operar: recorriendo la lista hasta encontrar el nodo al que apunta el iterador: O(n) incluyendo en la implementación del iterador un puntero a la lista a la que pertenece: O(1) Lista num primero último e 1... e n lista anterior Iterador Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 58
TAD Lista Ejemplo de mejora de la eficiencia usando listas con iteradores: Se desea saber el número de veces que aparece un elemento en una lista template <typename T> int ocurrencias(const Lista<T>& L, const T& elemento) { } int cont = 0; for (int i = 1 ; i <= L.longitud(); i++) if (L.observar(i) == elemento) cont++; return (cont); (a) acceso por índice template <typename T> int ocurrencias(const Lista<T>& L, const T& elemento) { } int cont = 0; for (typename Lista<T>::Iterador it = L.principio(); it!= L.final(); it.avanzar(l)) if (it.observar(l) == elemento) cont++; return (cont); (b) acceso con iteradores Implementación dinámica con índices: Implementación dinámica con iteradores: Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 59
TAD Lista 2.4.4 Los contenedores vector y list de la STL Características del contenedor vector: Es una secuencia que soporta acceso aleatorio en tiempo a constante a sus elementos mediante un índice Las inserciones y eliminaciones en el final se realizan en un tiempo de ejecución O(1) Las inserciones y eliminaciones al comienzo o en medio del vector tienen un coste temporal lineal El número de elementos en el vector puede variar de forma dinámica (la administración de memoria es automática) Características del contenedor list: Está implementado como una lista doblemente enlazada permite recorridos hacia delante y hacia atrás (define iteradores bidireccionales) Las inserciones y eliminaciones en los extremos se realizan en un tiempo de ejecución O(1) Las inserciones no invalidan los iteradores a elementos de la lista. Las eliminaciones sólo invalidan los iteradores que referencian a los elementos eliminados Las operaciones de borrado o eliminación pueden cambiar los predecedores o sucesores de los iteradores de una lista También existe el contenedor slist, que implementa una lista simplemente enlazada Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 60
TAD Lista El contenedor vector de la STL Operaciones básicas: vector() vector(const vector&) vector& operator=(const vector&) bool empty() const int size() const const tipo_base& operator[](int n) const void push_back(const T&) void pop_back() iterator begin() iterator end() iterator insert(iterator pos, const tipo_base& x) iterator erase(iterator pos) constructor constructor de copia operador de asignación esvacia longitud observar añadirder eliminarder Uso: #include <vector> // vector<tipo_base> v; devuelve un iterador al comienzo del vector devuelve un iterador al final del vector inserta x delante de pos elimina el elemento que ocupa la posición pos Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 61
TAD Lista El contenedor list de la STL Operaciones básicas: list() list(const list&) list& operator=(const lista&) bool empty() const int size() const iterator begin() iterator end() iterator insert(iterator pos, const tipo_base& x) iterator erase(iterator pos) Uso: #include <list> // list<tipo_base> l; constructor constructor de copia operador de asignación esvacia longitud devuelve un iterador al comienzo del vector devuelve un iterador al final del vector inserta x delante de pos elimina el elemento que ocupa la posición pos Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 62
TAD Lista El concepto de bidirectional iterator de la STL Expresiones válidas: *i *i = e ++i / i++ --i / i-- devuelve el elemento referenciado por el iterador i modifica el elemento referenciado por i avanza el iterador retrocede el iterador Estructuras de Datos II I.T. en Informática de Gestión/Sistemas Universidad de Huelva 63