Tipo Abstracto de s TAD Pila y TAD Cola 1. El Tipo Abstracto de s. 2. Definición de un TAD Pila. 3. Operaciones sobre el TAD Pila. 4. Implementación dinámica del TAD Pila en C. 5. Definición de un TAD Cola. 6. Operaciones sobre el TAD Cola. 7. Implementación dinámica del TAD Cola en C. 8. Aplicaciones del TAD Pila y del TAD Cola. 9. Ejercicios. Página 1
Tipos Abstractos de s, TAD Pila y TAD Cola 1. El Tipo Abstracto de datos El Tipo Abstracto de s o simplemente TAD, es una característica que proveen algunos lenguajes de programación. Un TAD se diferencia de los tipos de datos básicos por ser un tipo de dato definido por el programador similar a las estructuras y registros. Sin embargo, va más allá del hecho de solo almacenar datos, también están implícitas las operaciones de manipulación, almacenamiento y lógica de funcionamiento. Un Tipo Abstracto de s es un modelo con un número de operaciones que afectan a ese modelo. Los TAD proporcionan numerosos beneficios al programador y se pueden resumir en: Permiten una mejor conceptualización y modelado del mundo real. Mejora la representación y la comprensibilidad Mejoran la robustez del sistema. Además, cuando se implementan por medio de Programación Orientada a Objetos (POO): Mejoran el rendimiento y las prestaciones para lenguajes tipeados optimizando el tiempo de compilación. Separan la implementación de la especificación. La mejora y modificación de la implementación no afecta a la interfaz pública del Tipo Abstracto de. Permiten la extensibilidad del sistema. Los componentes de software son reutilizables y más fáciles de crear y mantener. Recogen la mejor semántica del tipo. Los Tipos Abstractos de s agrupan las operaciones y la representación de atributos o propiedades. Para comprender estos conceptos, lo mejor es llevarlos a la práctica. No todos los lenguajes de programación disponen del tipo de dato número complejo y de las operaciones básicas que pueden realizarse con este tipo de dato. Entonces, sería muy interesante crear el tipo de dato que almacene un número complejo y definir las operaciones que se pueden realizar sobre los complejos. Un número complejo tiene la siguiente forma: 25.34 + 45.72i parte real y parte imaginaria Para almacenar los datos de este número complejo se puede utilizar una estructura que contenga dos campos: Numero Real Imaginario 25.34 45.72 Algunas de las operaciones que se pueden efectuar con los números complejos son la suma y la resta. La condición para realizar estas operaciones es que se opera la parte real y la parte imaginaria de forma separada. Entonces si tenemos dos números complejos la suma y la resta se realizarán de la siguiente manera: Página 2
Computación para Ingenierías II Ing. Franz Mercado Lorberg Suma 25.34 + 45.72i + 13.62 + 52.12i 38.96 + 97.84i Resta 25.34 + 45.72i 13.62 + 52.12i 11.72 6.4 i La implementación para el almacenamiento y operaciones de los números complejos con la programación estructurada en C es: #include <stdio.h> // Definición de la estructura struct Complejo float Real; float Imaginario; ; // Prototipos de funciones struct Complejo Carga_s( void ); struct Complejo Suma(struct Complejo Num1, struct Complejo Num2 ); struct Complejo Resta(struct Complejo Num1, struct Complejo Num2 ); void Muestra(struct Complejo Num ); void main( void ) // Declaración de tres variables de tipo estructura Complejo struct Complejo Numero1, Numero2, Resultado; Numero1 = Carga_s(); Numero2 = Carga_s(); Resultado = Suma( Numero1, Numero2); printf("\n\nla suma de: "); Muestra( Numero1 ); printf("\ny de: "); Muestra( Numero2 ); printf("\nda como resultado: "); Muestra( Resultado ); Resultado = Resta( Numero1, Numero2); printf("\n\nla resta de: "); Muestra( Numero1 ); printf("\ny de: "); Muestra( Numero2 ); printf("\nda como resultado: "); Muestra( Resultado ); Página 3
Tipos Abstractos de s, TAD Pila y TAD Cola struct Complejo Carga_s( void ) struct Complejo Num; printf("\nintroduzca la parte real del complejo:\n"); scanf("%f", &Num.Real ); printf("introduzca la parte imaginaria del complejo:\n"); scanf("%f", &Num.Imaginario ); return Num; struct Complejo Suma(struct Complejo Num1, struct Complejo Num2 ) struct Complejo Res; Res.Real = Num1.Real + Num2.Real; Res.Imaginario = Num1.Imaginario + Num2.Imaginario; return Res; struct Complejo Resta(struct Complejo Num1, struct Complejo Num2 ) struct Complejo Res; Res.Real = Num1.Real - Num2.Real; Res.Imaginario = Num1.Imaginario - Num2.Imaginario; return Res; void Muestra(struct Complejo Num ) if ( Num.Imaginario >= 0 ) printf("%5.2f + %5.2fi", Num.Real, Num.Imaginario ); else printf("%5.2f - %5.2fi", Num.Real, -Num.Imaginario ); 2. Definición de un TAD Pila Este Tipo Abstracto de s permite almacenar datos de acuerdo a la siguiente lógica: Último en entrar, Primero en Salir (en inglés es LIFO Last In First Out). En la vida real podemos apreciar este comportamiento cuando se apilan elementos del mismo tipo, por ejemplo una pila de platos. Cuando se apila un nuevo plato este irá en la cima de los anteriores platos. Cuando se saca un plato para lavarlo, lógicamente saldrá el plato que se encuentra en la cima, sino se romperá la estructura de apilamiento de platos y lo más probable es que se rompan los platos también... En inglés una Pila se denomina stack. Página 4
Computación para Ingenierías II Ing. Franz Mercado Lorberg La imagen siguiente muestra una pila de platos: C B A Los platos están rotulados para identificarlos mejor. El plato C se encuentra en la cima de la pila. Importante: El elemento que se encuentra en la cima se denomina el de la pila. 3. Operaciones sobre un TAD Pila Existen dos operaciones fundamentales en el TAD Pila: Apilar consiste en agregar un nuevo elemento a la pila. Esta operación se conoce en inglés como Push. Des-apilar consiste en quitar de la pila el elemento que se encuentra al. Esta operación se conoce en inglés como Pop. Importante: La pila inicialmente está vacía. Veamos con un ejemplo como funciona el apilamiento: (Paso 1) (Paso 2) (Paso 3) (Paso 4) C B B A A A La pila inicialmente está vacía. No existe. Se agrega el primer elemento. Por ser el único elemento, A se convierte en el tope de la pila. Se agrega el segundo elemento. B se ubica encima de A y se convierte en el nuevo tope. Se agrega el tercer elemento. C se ubica encima de B y se convierte en el nuevo tope. En general el nuevo elemento que va a ingresar a la pila se ubica por encima del y además se convierte en el nuevo. La única excepción es entre el primer y segundo paso donde no existe debido a que la pila está vacía. Página 5
Tipos Abstractos de s, TAD Pila y TAD Cola Veamos con un ejemplo como funciona el des-apilamiento: (Paso 1) (Paso 2) (Paso 3) (Paso 4) C B B A A A La pila contiene elementos. El es C. Se quita el elemento C que está en el. El nuevo tope es B que es el elemento anterior a C. Se quita el elemento B que está en el. El nuevo tope es A que es el elemento anterior a B. Se quita el elemento A que está en el. No hay nuevo pues la Pila se ha vaciado. En general el elemento que va a quitar de la pila es el que está en el y elemento anterior se convierte en el nuevo. La única excepción es cuando ya no hay más elementos para quitar. 4. Implementación dinámica del TAD Pila en C Para implementar el TAD Pila se va a requerir de estructuras que permitan modelar los elementos que componen la pila. Los elementos de la pila se denominan Nodos. La definición de la estructura constará de las siguientes partes: struct Nodo char ; struct Nodo *; ; Nodo Observe que el campo se utilizará para almacenar el elemento. El campo es un puntero que se utilizará para apuntar al anterior elemento de la pila (como el anterior elemento es de tipo struct Nodo, esta definición es apropiada). Para saber en todo momento cuál es el de la pila se utilizará un puntero global. Importante: Cuando un puntero no apunta a nada se utiliza NULL (el nulo de los punteros). Para especificar que pila está vacía se tendrá una expresión similar a: struct Nodo * = NULL; NULL Página 6
Computación para Ingenierías II Ing. Franz Mercado Lorberg El objetivo de esta combinación de punteros y estructuras para representar la pila A, B y C será similar al la siguiente imagen: De esta forma, se entiende que el elemento que está en el es. Que el elemento anterior a es y el elemento anterior a es. Importante: Se ha utilizado una para representar el hecho de que no existe un elemento anterior a. Esta equis se implementará en el código como un puntero nulo (NULL). Ahora que se ha comprendido como se va a implementar el TAD Pila en C, veamos el código: #include <stdio.h> // Definición de la estructura struct Nodo char ; struct Nodo *; ; // Puntero global que indica donde está el. // Inicialmente la pila está vacía, entonces apunta a NULL. struct Nodo * = NULL; // Prototipos de funciones void Inserta Pila( char Letra ); void Muestra( void ); void main( void ) // Inserta algunos elementos en el TAD Pila Inserta Pila(); Inserta Pila(); Inserta Pila(); // Despliega todos los elementos almacenados en el TAD pila Muestra(); Página 7
Tipos Abstractos de s, TAD Pila y TAD Cola // Funciones para el manejo del TAD Pila void Inserta Pila( char Letra ) struct Nodo *; = (struct Nodo*) malloc(sizeof(struct Nodo)); // Verifica si pudo reservar espacio en la memoria if ( == NULL ) return; // Si no pudo sale de la función else // Si pudo reservar, carga el nodo -> = Letra; -> = NULL; // Si no hay el nuevo se convierte en // el nuevo. Si ya había un, se lo // enlaza como anterior elemento y el // se convierte en el nuevo. if ( == NULL ) = ; else -> = ; = ; void Muestra(void) struct Nodo *Aux; // Si no hay no se muestra nada, sino se recorre el // TAD Pila y se muestran los elementos que lo componen. if ( == NULL ) printf("\nel TAD Pila esta vacío..."); else printf("\nel TAD Pila está compuesto por:\n"); Aux = ; while( Aux!= NULL ) printf("\nelemento: %c", Aux->); Aux = Aux->; Gráficamente se representaría como: Nodo Definición del molde, modelo ó plantilla Página 8
Computación para Ingenierías II Ing. Franz Mercado Lorberg En base a la definición del modelo se crea la variable global puntero del tipo Nodo. Inicialmente apunta a NULL porque el TAD Pila está vacío. NULL (Paso 1) Cuando se llama a la función Inserta Pila( ) con el valor, se intenta reservar un espacio de memoria. En caso de tener éxito la variable queda apuntado al nuevo espacio. NULL El valor está almacenado en la variable Letra, así es que se asigna Letra al campo apuntado por. El campo se llena con un NULL pues aún no se ha determinado si existe un elemento anterior. NULL Luego se pregunta si el es igual a NULL. Si esto se cumple es porque el TAD Pila está vacío y el nuevo elemento debe convertirse en el nuevo. (Paso 2) Al llamar a la función Inserta Pila( ) con el valor, se intenta reservar un espacio de memoria. En caso de tener éxito la variable queda apuntado al nuevo espacio. Página 9
Tipos Abstractos de s, TAD Pila y TAD Cola El valor está almacenado en la variable Letra, así es que se asigna Letra al campo apuntado por. El campo se llena con un NULL pues aún no se ha determinado si existe un elemento anterior. Luego se pregunta si el es igual a NULL. Si esto NO se cumple es porque el TAD Pila ya contiene uno o más elementos. Así es que hay que enlazar el campo anterior de con el. Ahora se tiene un nuevo elemento que está por encima de los demás, el puntero tendrá que desplazarse al mismo lugar donde apunta. (Paso 3) Al llamar a la función Inserta Pila( ) con el valor, se intenta reservar un espacio de memoria. En caso de tener éxito la variable queda apuntado al nuevo espacio. Página 10
Computación para Ingenierías II Ing. Franz Mercado Lorberg El valor está almacenado en la variable Letra, así es que se asigna Letra al campo apuntado por. El campo se llena con un NULL pues aún no se ha determinado si existe un elemento anterior. Luego se pregunta si el es igual a NULL. Si esto NO se cumple es porque el TAD Pila ya contiene uno o más elementos. Así es que hay que enlazar el campo anterior de con el. Ahora se tiene un nuevo elemento que está por encima de los demás, el puntero tendrá que desplazarse al mismo lugar donde apunta. Finalmente el TAD Pila contiene C, B y A. Página 11
Tipos Abstractos de s, TAD Pila y TAD Cola Importante: Seguramente ha notado que la variable desaparece cada vez. Esto se debe a que es una variable local y cuando termina de ejecutarse la función Inserta Pila( ) donde ha sido declarada, la variable se descarga de la memoria. Para desplegar los elementos almacenados en el TAD Pila, se utiliza una variable local de tipo puntero a estructura Aux que comienza en el lugar donde está el. Aux Se emplea un ciclo para ir desplazando Aux a través del TAD. Cada vez que se ejecuta la instrucción: Aux = Aux->; Provoca que Aux vaya al nodo anterior. Aux En determinado momento el puntero Aux saltará a NULL (sale fuera de la estructura). Esto causará que el bucle o ciclo while se detenga: while( Aux!= NULL ) Aux NULL Página 12
Computación para Ingenierías II Ing. Franz Mercado Lorberg 5. Definición de un TAD Cola Este Tipo Abstracto de s permite almacenar datos de acuerdo a la siguiente lógica: Primero en entrar, Primero en Salir (en inglés es FIFO First In First Out). En la vida real podemos apreciar este comportamiento cuando se realiza una cola de personas que desean ser atendidas por un cajero en el banco. Cuando el cajero atiende a una persona, esta será la que se encuentra al inicio de la cola. Cuando una nueva persona llega, deberá ubicarse al final de la cola sino romperá la estructura de cola y es posible que las personas que estaban primero se molesten y quieran tomar represalias verbales ó físicas con el nuevo En inglés una Cola se denomina queu. 6. Operaciones sobre el TAD Cola Existen dos operaciones fundamentales en el TAD Cola: Agregar Sacar un nuevo elemento a la cola. El nuevo elemento va al final de la cola a menos que sea el único y automáticamente será el inicio de la cola. un elemento existente de la cola. El elemento que sale es el primero de la cola. La única excepción es que no existan elementos en la cola. Importante: La cola inicialmente está vacía. 7. Implementación dinámica del TAD Cola en C La implementación el TAD Cola también requerirá de estructuras que permitan modelar los elementos que componen la cola. Los elementos de la cola también se denominan Nodos. La definición de la estructura constará de las siguientes partes: struct Nodo char ; struct Nodo *Sig; ; Nodo Sig Observe que el campo se utilizará para almacenar el elemento. El campo Sig es un puntero que se utilizará para apuntar al siguiente elemento de la cola (como el siguiente elemento es de tipo struct Nodo, esta definición es apropiada). Página 13
Tipos Abstractos de s, TAD Pila y TAD Cola Para saber en todo momento cuál es el Inicio de la cola se utilizará un puntero global. Para especificar que la cola está vacía se tendrá la expresión: struct Nodo *Inicio = NULL; Inicio NULL Si se insertan los elementos A, B y C en un TAD cola se tendrá la siguiente imagen: Inicio Sig Sig Sig De esta manera, se entiende que el elemento que está en al inicio es. Que el siguiente elemento después de es y el siguiente elemento después de es. Importante: Se ha utilizado una para representar el hecho de que no existe ningún elemento después de. Esta equis se implementará en el código como un puntero nulo (NULL). Vamos al código: #include <stdio.h> // Definición de la estructura struct Nodo char ; struct Nodo *Sig; ; // Puntero global que indica donde está el Inicio. // Inicialmente la cola está vacía, entonces Inicio apunta a NULL. struct Nodo *Inicio = NULL; // Prototipos de funciones void Inserta Cola( char Letra ); void Muestra( void ); void main( void ) // Inserta algunos elementos en el TAD Cola Inserta Cola(); Inserta Cola(); Inserta Cola(); // Despliega todos los elementos almacenados en el TAD cola Muestra(); Página 14
Computación para Ingenierías II Ing. Franz Mercado Lorberg // Funciones para el manejo del TAD Cola void Inserta Cola( char Letra ) struct Nodo *, *Ultimo; = (struct Nodo*) malloc(sizeof(struct Nodo)); // Verifica si pudo reservar espacio en la memoria if ( == NULL ) return; // Si no pudo sale de la función else // Si pudo reservar, carga el nodo -> = Letra; ->Sig = NULL; // Si no hay Inicio el nuevo se convierte en // el nuevo Inicio. Si ya había Inicio, hay que // desplazarse hasta el último elemento y enlazar // el último con el. if ( Inicio == NULL ) Inicio = ; else // Desplaza Ultimo hasta el // último elemento Ultimo = Inicio; while( Ultimo->Sig!= NULL ) Ultimo = Ultimo->Sig; // Enlaza el último con el nuevo Ultimo->Sig = ; void Muestra(void) struct Nodo *Aux; // Si no hay Inicio no se muestra nada, sino se recorre el // TAD Cola y se muestran los elementos que lo componen. if ( Inicio == NULL ) printf("\nel TAD Cola esta vacío..."); else printf("\nel TAD Cola está compuesto por:\n"); Aux = Inicio; while( Aux!= NULL ) printf("\nelemento: %c", Aux->); Aux = Aux->Sig; Página 15
Tipos Abstractos de s, TAD Pila y TAD Cola 8. Aplicaciones del TAD Pila y del TAD Cola Una de las aplicaciones más típicas del TAD Pila es almacenar los elementos de de que consta una expresión aritmética que está formada por operadores y operandos, con el fin de evaluar el valor numérico de dicha expresión. Para una explicación completa de este método de evaluación lea el texto Estructura de s. Algoritmos y abstracción y objetos de Luís Joyanes Aguilar e Ignacio Zahonero, McGraw-Hill, 1999, página 215. Los TAD Cola son de carácter más general pues se pueden aplicar a sistemas de pedidos, sistemas de inventarios, sistemas de atención de clientes (bancos e instituciones donde es fundamental el orden de llegada) e inclusive adaptar el comportamiento de la cola a una cola de prioridades. Para tener una mayor referencia busque información en el texto Estructura de s. Algoritmos y abstracción y objetos de Luís Joyanes Aguilar e Ignacio Zahonero, McGraw-Hill, 1999, página 247. 9. Ejercicios Resuelva los siguientes problemas: 1. Registre en un TAD Pila los datos: 33, 44, 11, 77, 55. Implemente las funciones requeridas para extraer un dato del TAD Pila y encontrar el dato más pequeño del TAD. Al extraer un dato, se elimina el nodo pero se retorna el valor numérico que se almacenaba en el nodo. 2. Almacene N sueldos (float) en un TAD Cola. Calcule cuanto se debe pagar por concepto de sueldos sin impuestos y cuanto se debe retener por concepto de impuestos. Considere los impuestos RC-IVA (13%) e IT (3%) que se aplican a todos los sueldos. 3. Registre en un TAD Pila los datos: 1, 2, 3, 4, 5, 6. Implemente las funciones requeridas para extraer todos los datos de la pila y enviarlos a un TAD Cola. Muestre los tres primeros datos del TAD Cola para verificar que los datos se han enviando apropiadamente. 4. Seleccione un tipo abstracto de datos para guardar en un calendario N nodos. Cada nodo podrá almacenar una fecha y una actividad que se realizará en esa fecha. Ej. 07-11-06, Realizar la práctica de computación II. 13-11-06, Examen de computación II. Cuando una actividad se cumpla deberá ser eliminada del calendario. 5. Implemente una aplicación que permita almacenar los siguientes datos que componen el registro de una persona: Nombre, edad y estatura. Ingrese N registros y luego muestre el contenido entero del TAD. 6. Elija el TAD apropiado para: a. Almacenar N ventas (deberá almacenar: Producto, precio y descuento). b. Mostrar las últimas tres ventas del día. c. Mostrar el contenido completo del TAD. d. Encontrar cuál fué la mayor de las ventas. Página 16