Tipos de Datos Abstractos Facultad de Ciencias de la Computación Juan Carlos Conde R. Object-Oriented Programming I
Contenido 1 Introducción 2 Estructura tipo LIFO 3 Estructura tipo FIFO 4 LISTAS Ligadas 1 / 51
Contenido 1 Introducción 2 Estructura tipo LIFO 3 Estructura tipo FIFO 4 LISTAS Ligadas 2 / 51
Estructuras de Datos Una estructura de datos es un conjunto de variables, quizá de tipos distintos, que se relacionan entre sí y que se pueden operar como un todo. Son fundamentales para el manejo de información y el desarrollo de sistemas. Se clasican en la forma como permiten el acceso a los datos del conjunto. Algunas imponen restricciones, tales como permitir el acceso sólo al elemento más reciente o al menos reciente insertado. Aquí se tratan tres estructuras interesantes y útiles para diversas aplicaciones: Pilas, Colas y Listas. 3 / 51
Contenido 1 Introducción 2 Estructura tipo LIFO 3 Estructura tipo FIFO 4 LISTAS Ligadas 4 / 51
PILAS El término LIFO es del acrónimo Last In First Out y una PILA es una estructura de este tipo. Una pila es una lista de elementos en la cual los elementos se insertan o se eliminan sólo por uno de los extremos, es decir, el primero en entrar es el último en salir. Se dice que los elementos que se guardan en una estructura de este tipo se apilan, así que no es posible tomar el de hasta abajo sin antes quitar los de encima. 5 / 51
Representación de una PILA Las pilas no están denidas como tales en los lenguajes de programación, éstas se representan mediante el uso de Arreglos o Listas ligadas. Los elementos de una pila permiten que la pila crezca (hacia arriba) o decrezca (hacia abajo). 6 / 51
Representación de una PILA Para el manejo de la pila se requiere esencialmente de: TOPE de la Pila. Es un apuntador (subíndice del arreglo) al último elemento almacenado en la pila. Este apuntador se mueve sobre el arreglo, hacia arriba o hacia abajo, según la pila crezca o decrezca. Debe recordarse que un arreglo es una estructura estática, así, de manera real el tope se movería de la siguiente manera sobre el recipiente que contiene a la pila: 7 / 51
Movimientos del Tope 8 / 51
Operaciones sobre una PILA En una pila se pueden hacer dos operaciones: Insertar: meter a la pila (PUSH) Eliminar: sacar de la pila (POP) NOTA: Los elementos se insertan y se eliminan por un solo extremo. El estado inicial del tope es 0 (cuando la pila esta vacía). Este se decrementa cuando se inserta información en la pila y se incrementa cuando se elimina información de la misma. 9 / 51
Operación Push I Esta operación inserta elementos en la pila si ésta aún no esta llena. La pila esta llena cuando no hay mas lugares en el arreglo para seguir insertando información. Por ejemplo: Suponga que max = 4, entonces Estado inicial de la pila: Si se inserta un 15, es decir, push(15) 10 / 51
Operación Push II Si se inserta un 7, es decir, push(7) Entonces el Push quedaría como sigue: push (Dato) Inicio Si Tope < MAX entonces Tope Tope + 1 Pila[Tope] Dato Sino Escribir Pila llena Fin_si 11 / 51
Operación Pop I Esta operación elimina elementos en la pila si ésta aún no está vacía. La pila esta vacía cuando no hay elementos en el arreglo (como en el estado inicial). Por ejemplo: Suponga que max = 4 y que la pila ya tiene 2 elementos. Si se elimina un elemento entonces: El elemento se saca del tope de la pila (Dato Pila[Tope]) y el Tope se incrementa. 12 / 51
Operación Pop II Entonces Pop quedaría: pop () Inicio Si Tope > 0 entonces Tope Tope - 1 Dato Pila[Tope] Sino Escribir Pila vacía Fin_si Retorna Dato 13 / 51
#include <iostream.h> #include <stdio.h> enum estados{vacia,normal,sin_espacio}; class pila //Clase pila estática { int stack[100]; int tope, max; enum estados edo; public: pila(int t) { tope = 0; max = t} void push(int dato); int pop(void); void visualizar(void); void ver_estado(void); void pon_estado(void); enum estados obten_estado(void) { return edo;} }; 14 / 51
Ejemplo 2: FIBONACCI ACTIVIDAD En base a lo analizado anteriormente, codica la implementación de los siguientes métodos: void push(int dato); int pop(void); void visualizar(void); void ver_estado(void); void pon_estado(void); En base a tu experiencia y tu conocimiento sobre Programación Orientada a Objetos, determina si realmente es necesario el método: Justica tu respuesta. enum estados obten_estado(void) { return edo;} 15 / 51
Contenido 1 Introducción 2 Estructura tipo LIFO 3 Estructura tipo FIFO 4 LISTAS Ligadas 16 / 51
COLAS Una cola es una estructura tipo FIFO (First In First Out), así que el primero en entrar es el primero en salir. Una cola es una lista de elementos en la cual los elementos se insertan por un extremo y se eliminan por otro, es decir, los elementos salen en el mismo orden en que entraron. Se dice que los elementos que se guardan en una estructura de este tipo se encolan uno después del otro, de la misma forma que funcionan las colas en los bancos o en los supermercados. 17 / 51
Representación de una COLA Las colas no están denidas como tales en los lenguajes de programación, éstas se representan mediante el uso de Arreglos o Listas ligadas. Se dice que un Arreglo es un buen recipiente para una cola. 18 / 51
Representación de una COLA Para manejarla se requiere de dos apuntadores (subíndices): 1. Uno que anote al último elemento almacenado en la cola, denominado FONDO. 2. Otro que apunte al primer elemento almacenado en la cola, denominado FRENTE. 19 / 51
Operaciones sobre una PILA En una cola se pueden llevar a cabo dos operaciones: Insertar: meter dato en la cola Eliminar: sacar dato de la cola Los elementos se insertan por un extremo y se eliminan por el otro. Si se supone el siguiente esquema: 20 / 51
Comportamiento de los apuntadores donde max es igual a la última posición del arreglo y min es igual a la primera posición del arreglo, el estado inicial del fondo y del frente es min 1, esto es: Así, el Fondo se incrementa en uno cada vez que un elemento se inserta en la cola y el Frente se incrementa en uno cada vez que un elemento se elimina de la cola. 21 / 51
Operación de Inserción Esta operación inserta un elemento al nal de la cola, para lo cual mueve el apuntador Fondo e inserta, siempre que exista espacio para la inserción. Inserta_cola (Dato) Inicio Si Fondo < max entonces Fondo Fondo + 1 Cola[Fondo] Dato Si Frente < min entonces [con la primera inserción se mueve el apuntador Frente para indicar que ya se ha insertado un elemento en la cola] Frente Frente + 1 Fin_si Sino Escribir Cola llena Fin_si Fin 22 / 51
Operación de Eliminación Esta operación elimina un elemento del Frente de la cola siempre y cuando la cola tenga elementos. Si el Frente es igual al Fondo signica que se ha eliminado el último elemento, de no ser asi, el apuntador Frente se mueve para anotar al próximo elemento a ser eliminado. Elimina_cola( ) Inicio Si Frente >= min entonces Dato Cola[Frente] Si Frente = Fondo entonces [si se elimina el último elemento entonces se vuelve al estado inicial (cola Vacía)] Fondo min-1 Frente Fondo sino Frente Frente + 1 Fin_si Sino Escribir Cola Vacía' Fin_si Fin 23 / 51
Código ejemplo #include <iostream.h> // Clase cola class Cola { int c[100]; int e, s; int tamanio; }; public: void inicia (void); void pondato_cola (int i); int sacadato_cola (void); void pon_tamanio(int t){tamanio = t;} 24 / 51
Código ejemplo /*Definicion de metodos */ void Cola::inicia(void) { e = s = 0; } void Cola::pondato_cola(int i) { if (e == tamanio) cout La cola esta llena endl; c[e++]=i; } Cola::sacadato_cola(void) { if (e == s) { cout La cola esta vacía; return 0; } return c[s++]; } 25 / 51
Código ejemplo int main(void) { Cola a, b; // Objetos de la clase Cola a.inicia(); b.inicia(); a.pon_tamanio(5); b.pon_tamanio(8); a.pondato_cola(10); a.pondato_cola(20); b.pondato_cola(100); b.pondato_cola(200); cout COLA A : a.sacadato_cola() endl; cout COLA A : a.sacadato_cola() endl; cout COLA A : a.sacadato_cola() endl; cout COLA B : b.sacadato_cola() endl; cout COLA B : b.sacadato_cola() endl; } return 0; 26 / 51
Contenido 1 Introducción 2 Estructura tipo LIFO 3 Estructura tipo FIFO 4 LISTAS Ligadas 27 / 51
Denición En el caso de las Pilas y Colas se emplean estructuras estáticas en dónde la manipulación de datos es a través de posiciones localizadas secuencialmente. Para declarar estas estructuras se debe denir un tamaño determinado el cual NO puede modicarse posteriormente. En algunas ocasiones es más conveniente utilizar estructuras dinámicas, en las cuales se puede aumentar o disminuir de tamaño de la estructura de acuerdo a los requerimientos especícos de la aplicación. 28 / 51
Denición Una lista ligada es un grupo de datos organizados secuencialmente, pero a diferencia de los arreglos, la organización no esta dada implícitamente por su posición en el arreglo. En una lista ligada cada elemento es un nodo que contiene el dato y además una liga al siguiente dato. Estas ligas son simplemente variables que contienen la(s) dirección(es) de los datos contiguos o relacionados. Para manejar una lista es necesario contar con un apuntador al primer elemento de la lista denominado raíz o cabeza. 29 / 51
Denición La ventaja principal de las listas ligadas es que: ½Permiten que sus tamaños cambien durante la ejecución del programa! Así, las listas se conocen como estructuras de datos dinámicas. 30 / 51
Operaciones En una lista ligada se pueden realizar básicamente 4 operaciones: 1. Recorrer: moverse sobre los elementos de la lista, partiendo del inicio y llegando al nal de la misma. 2. Insertar: añadir elementos a la lista. 3. Eliminar: quitar elementos de la lista. 4. Buscar: vericar la existencia de un elemento dado dentro de la lista. La inserción y la eliminación se pueden llevar a cabo: Al inicio de la lista Al nal de la lista Entre dos elementos de la lista 31 / 51
Notación Los nodos son almacenados en un espacio de memoria denominado Memoria del Montón (Heap), esta memoria es asignada por el procesador, a solicitud del programa, en tiempo de ejecución. Cuando la memoria es asignada se obtiene un apuntador (dirección) al elemento, así, se usa Crea_nodo(P) para indicar la asignación de memoria dinámica que es apuntada por P, esto es: 32 / 51
Notación Para referirse al dato almacenado en info o en liga se utiliza la notación: P.info P.liga Para indicar que el apuntador liga anota a nada se utiliza: 33 / 51
Notación Así el procedimiento del caso base, donde se crea un nuevo nodo para una lista que está vacía sería como: 34 / 51
Listas Simplemente Ligadas Una lista sencillamente ligada es un grupo de datos en dónde cada dato contiene un apuntador hacia el siguiente dato en la lista, es decir, una liga hacia el siguiente elemento. 35 / 51
Operación de Inserción Para insertar un dato en una lista simplemente ligada es necesario crear un nuevo nodo, recorrer la lista nodo por nodo hasta encontrar la posición adecuada y actualizar las ligas para insertar el nuevo nodo. CASO 1: Lista Desordenada Supóngase que se tiene una lista donde los datos se insertan siempre al nal de la misma sin importar el orden. En esta caso el algoritmo de inserción es el siguiente... 36 / 51
Operación de Inserción Inserción_lista_sencilla_desordenada(dato) Inicio Crea_nodo(P) P.info dato P.liga NULO Si Raíz = NULO entonces [Se coloca apuntador Raíz al primer elemento de la lista] Raíz P Sino [Se recorre la lista para encontrar la última posición] recorre Raíz Mientras recorre.liga <> NULO hacer recorre recorre.liga Fin_mientras [Se liga el nuevo nodo en la última posición] recorre.liga P Fin_si Fin 37 / 51
Operación de Inserción CASO 2: Lista Ordenada Si lo que se quiere es tener una lista donde los datos están ordenados ascendente o descendentemente, entonces el algoritmo cambia ligeramente. Aquí, durante el recorrido se verica que los nodos que se recorren tengan datos menores o mayores (según el caso) que el dato que se va a insertar. El algoritmo para una lista de datos ordenados de manera ascendente, es el siguiente... 38 / 51
Operación de Inserción Inserción_lista_sencilla_ordenada(dato) Inicio Crea_nodo(P) P.info dato [Si es el primer elemento de la lista] Si Raíz = NULO entonces [Se coloca apuntador Raíz al primer elemento de la lista] Raíz P Raíz.liga NULO Sino [Se recorre la lista para encontrar la posición correcta donde debe insertarse el nuevo nodo] recorre Raíz anterior recorre Mientras(recorre.liga<>NULO) y (recorre.info < dato) hacer anterior recorre recorre recorre.liga Fin_mientras 39 / 51
Operación de Inserción [Se verifica si la inserción es al inicio de la lista, al final o entre dos nodos para realizar las operaciones pertinentes] Si recorre.info < dato entonces [Se inserta al final] recorre.liga P p.liga NULO Sino Si recorre = Raíz entonces [insertar al inicio] Raíz P Raíz.liga recorre Sino [Se inserta entre dos nodos] anterior.liga P P.liga recorre Fin_si Fin_si Fin_si Fin 40 / 51
Operación de Inserción Es importante notar el uso de dos apuntadores durante el recorrido: anterior y recorre. anterior siempre esta anotando al nodo que precede al anotado por recorre, de tal forma que cuando se deba insertar entre dos nodos, se tengan apuntadores a los dos nodos entre los que se insertará, esto se ilustra a continuación: Suponga que se tiene la lista... 41 / 51
Operación de Inserción y que se desea insertar 10, entonces el proceso es: 42 / 51
Operación de Inserción En este momento la condición (recorre.liga<>nulo) y (recorre.info < dato) ya no se cumple y termina el recorrido, entonces se verica si debe insertarse al inicio, al nal o entre dos nodos. No se cumple al inicio (recorre <> Raíz), no es al nal (recorre.liga = NULO, pero recorre.info no es menor que dato), entonces es entre dos nodos: 43 / 51
Operación de Eliminación Para eliminar un dato de una lista sencillamente ligada es necesario recorrer la lista haciendo una búsqueda del elemento a eliminar: Mientras(recorre.liga<>NULO) y (recorre.info <> dato) hacer anterior recorre recorre recorre.liga Fin_mientras Aquí se pueden tener dos opciones: 44 / 51
Operación de Eliminación A) Encontrar el elemento al inicio, al nal o entre dos nodos. Suponga que se desea eliminar 5 (inicio de la lista): Entonces recorre se queda en la Raíz y la eliminación se realiza de la siguiente manera: Esto es: Si recorre.info = dato entonces Si recorre = Raíz entonces [se elimina de la Raíz] Raíz recorre.liga 45 / 51
Operación de Eliminación Suponga que desea eliminar 11 (nal de la lista): Esto es: Si recorre.info = dato entonces Si recorre = Raíz entonces [se elimina de la Raíz] Raíz recorre.liga Sino Si (recorre.liga = NULO)) entonces [esta al final de la lista] anterior.liga NULO 46 / 51
Operación de Eliminación Suponga que desea eliminar 7 (entre dos nodos): 47 / 51
Operación de Eliminación Esto es: Si recorre.info = dato entonces Si recorre = Raíz entonces [se elimina de la Raíz] Raíz recorre.liga Sino Si (recorre.liga = NULO) entonces [esta al final de la lista] anterior.liga NULO Sino [Esta entre dos nodos] anterior.liga recorre.liga Fin_si Fin_si Fin_si Pero como el último elemento de la lista apunta a NULO, entonces se puede reducir el algoritmo: Si recorre.info = dato entonces Si recorre = Raíz entonces [se elimina de la Raíz] Raíz recorre.liga Sino [si esta al final o entre dos nodos, se recorren las ligas] anterior.liga recorre.liga Fin_si Fin_si 48 / 51
Operación de Eliminación B) Llegar al nal de la lista sin encontrar el dato a eliminar. Si recorre.info = dato entonces Si recorre = Raíz entonces [se elimina de la Raíz] Raíz recorre.liga Sino [si esta al final o entre dos nodos, se recorren las ligas] anterior.liga recorre.liga Fin_si Sino [llego al final sin encontrar el dato] Escribir Dato no encontrado Fin_si 49 / 51
Operación de Eliminación Algoritmo completo: Eliminación_lista_sencilla(dato) Inicio Si Raíz <> NULO entonces recorre Raíz Mientras(recorre.liga<>NULO) y (recorre.info <> dato) hacer anterior recorre recorre recorre.liga Fin_mientras Si recorre.info = dato entonces [lo encontro] SI recorre = Raíz entonces [se elimina de la Raíz] Raíz recorre' libera_nodo(recorre) Sino [si esta al final o entre dos nodos, se recorren las ligas] anterior.liga recorre.liga libera_nodo(recorre) Fin_si Sino [no lo encontro] Escribir Dato no encontrado Fin_si Sino Escribir Lista Vacía Fin_si Fin 50 / 51
Sólo podemos ver poco del futuro pero lo suciente para darnos cuenta que hay mucho que hacer. [Alan Turing] Juan Carlos Conde R. juanc.conde@cs.buap.mx 51 / 51