From 9e79d0ffcd8eca4a43e263299b9fb12f9439128c Mon Sep 17 00:00:00 2001 From: Manuel Vergara Date: Fri, 21 Jun 2024 21:02:20 +0200 Subject: [PATCH] Update PEC6 --- fundamentos-programacion/PEC6/README.md | 690 +++++++++++++++++++++++- 1 file changed, 688 insertions(+), 2 deletions(-) diff --git a/fundamentos-programacion/PEC6/README.md b/fundamentos-programacion/PEC6/README.md index 3b70bc9..2837ee5 100644 --- a/fundamentos-programacion/PEC6/README.md +++ b/fundamentos-programacion/PEC6/README.md @@ -4,7 +4,7 @@ **Índice** - [PEC 6](#pec-6) - - [18. Tipos abstractios de datos](#18-tipos-abstractios-de-datos) + - [18. Tipos abstractos de datos](#18-tipos-abstractos-de-datos) - [18.1. Tipo abstracto de datos (TAD)](#181-tipo-abstracto-de-datos-tad) - [18.1.1. Ejemplos de especificación e implementación](#1811-ejemplos-de-especificación-e-implementación) - [18.1.1.1. Números naturales](#18111-números-naturales) @@ -25,11 +25,20 @@ - [18.2.4. Sintaxis para la declaración (Definición de un TAD de tipo pila, cola o lista)](#1824-sintaxis-para-la-declaración-definición-de-un-tad-de-tipo-pila-cola-o-lista) - [19. Navegación de TAD](#19-navegación-de-tad) - [19.1. Ejemplos sobre el TAD pila](#191-ejemplos-sobre-el-tad-pila) + - [19.1.1 Ejemplo 1](#1911-ejemplo-1) + - [19.1.2 Ejemplo 2](#1912-ejemplo-2) + - [19.1.3 Ejemplo 3](#1913-ejemplo-3) - [19.2. Ejemplos sobre el TAD cola](#192-ejemplos-sobre-el-tad-cola) + - [19.2.1 Ejemplo 1](#1921-ejemplo-1) + - [19.2.2 Ejemplo 2](#1922-ejemplo-2) + - [19.2.3 Ejemplo 3](#1923-ejemplo-3) - [19.3. Ejemplos sobre el TAD lista](#193-ejemplos-sobre-el-tad-lista) + - [19.3.1 Ejemplo 1](#1931-ejemplo-1) + - [19.3.2 Ejemplo 2](#1932-ejemplo-2) + - [19.3.3 Ejemplo 3](#1933-ejemplo-3) -## 18. Tipos abstractios de datos +## 18. Tipos abstractos de datos Vamos a ver los tipos abstractos de datos (TAD) y cómo se pueden implementar para representar secuencias de elementos como Listas, Colas y Pilas. Estos TADs emulan conceptos que encontramos fuera del mundo de la programación. @@ -169,6 +178,22 @@ En general, un TAD que representa secuencias de elementos posee las siguientes o ¿Dónde insertar un elemento? ¿Qué elemento eliminar? ¿Qué elemento se puede consultar? ¿Cuántos elementos hay? Las respuestas a las anteriores preguntas son las que definen el comportamiento de las operaciones y, por lo tanto, el tipo de secuencia. +Tabla comparativa de las diferencias entre Pila, Cola y Lista: + +| Característica | Pila (Stack) | Cola (Queue) | Lista (List) | +| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Estructura de Datos** | Implementada típicamente con un arreglo estático o una lista enlazada. | Puede ser implementada con un arreglo estático o una lista enlazada. | Generalmente implementada con una lista enlazada o estructura dinámica. | +| **Principio de Acceso** | LIFO (Last In, First Out): Último en entrar, primero en salir. | FIFO (First In, First Out): Primero en entrar, primero en salir. | Acceso Aleatorio: Acceso directo a cualquier elemento. | +| **Operaciones Básicas** | `push(elemento)`: Añade un elemento al tope de la pila.
`pop()`: Elimina y retorna el elemento del tope de la pila.
`isEmpty()`: Verifica si la pila está vacía.
`isFull()`: Verifica si la pila está llena (en caso de implementación estática). | `enqueue(elemento)`: Añade un elemento al final de la cola.
`dequeue()`: Elimina y retorna el elemento al frente de la cola.
`isEmpty()`: Verifica si la cola está vacía.
`isFull()`: Verifica si la cola está llena (en caso de implementación estática). | `insertar(elemento)`: Inserta un elemento en la lista en una posición específica o al final.
`eliminar(elemento)`: Elimina un elemento de la lista.
`buscar(elemento)`: Busca un elemento específico en la lista. | +| **Implementación Común en C** | Puede ser implementada usando un arreglo estático con un índice que indica el tope, o con una lista enlazada donde cada nodo contiene el dato y un puntero al siguiente nodo. | Similar a la pila, puede usar un arreglo estático o una lista enlazada. En la implementación de arreglo, se usa un índice para controlar el frente y el final de la cola. | Implementada típicamente usando una lista enlazada donde cada nodo contiene el dato y un puntero al siguiente nodo, permitiendo inserciones y eliminaciones eficientes en cualquier posición. | +| **Eficiencia** | - Operaciones de `push()` y `pop()` son O(1) en una implementación con arreglo o lista enlazada.
- Espacio requerido puede ser menos flexible que una lista enlazada si se usa un arreglo estático. | - Operaciones de `enqueue()` y `dequeue()` son O(1) en una implementación con arreglo o lista enlazada.
- Requiere más espacio que una pila debido al almacenamiento de referencias de frente y final. | - Inserciones y eliminaciones pueden ser O(1) o O(n), dependiendo de si se inserta o elimina al principio o final de la lista.
- Búsqueda es O(n) en una lista simple enlazada sin optimizaciones adicionales. | +| **Uso Típico** | - Manejo de historial de llamadas (undo/redo).
- Evaluación de expresiones matemáticas (notación polaca inversa). | - Gestión de tareas en una cola (por ejemplo, impresión en una impresora).
- Algoritmos de búsqueda en grafos (BFS utiliza una cola para gestionar los nodos a visitar). | - Almacenamiento de datos que requieren acceso y modificación frecuente, pero no necesariamente en orden específico.
- Implementación de estructuras de datos más complejas como listas enlazadas dobles o listas circulares. | + +Explicación de términos comunes: +- **Arreglo estático**: Un arreglo con tamaño fijo determinado en tiempo de compilación. +- **Lista enlazada**: Una estructura de datos dinámica donde cada elemento (nodo) contiene el dato y un puntero al siguiente nodo. +- **Acceso Aleatorio**: Capacidad de acceder a cualquier elemento de la estructura de datos en tiempo constante (O(1)). + #### 18.2.1. El TAD Pila (tStack) @@ -927,15 +952,676 @@ nombreTipoLista = tList(tipolemento) ### 19.1. Ejemplos sobre el TAD pila +#### 19.1.1 Ejemplo 1 + +Se pide el diseño de una acción que muestre la representación binaria de un número recibido como parámetro. Se debe usar una pila que almacene los resultados de las divisiones parciales por la base (que es 2). Por ejemplo, si la entrada es 77, la salida debe ser 1001101. + +```alg +type + tBinaryStack = tStack(tBit) +end type + +action dev2bin(in n: integer) + var + b:tBinaryStack; + res, bit: integer; + end var + + initStack(b); + res := n; + + while res ≠ 0 do + bit := res mod 2; + res := res / 2; + push(b, bit); + end while + + while not isEmptyStack(b) do + top(b, bit); + write(bit); + pop(b); + end while + +end action +``` + +```c +#include + +typedef struct { + tBit A[MAXBITS]; + int nelem; +} tBinaryStack; + +void dev2bin(int n) { + tBinaryStack b; + int res, bit; + + initStack(&b); + res = n; + + while (res != 0) { + bit = res % 2; + res = res / 2; + push(&b, bit); + } + printf("\n"); + + while (!isEmptyStack(b)) { + top(b, &bit); + printf("%d", bit); + pop(&b); + } + printf("\n"); + +} +``` + + +#### 19.1.2 Ejemplo 2 + +Se modela el historial de accesos a un conjunto de páginas web. La última página accedida está en la cima de la pila y si nos movemos hacia atrás en el historial iremos accediendo en orden desde el acceso más reciente al más antiguo. + +En el historial cada página tiene la siguiente info: +- Identificador único de tipo entero. +- Una dirección de memoria donde se encuentra el código de la página +- La fecha de último acceso en formato aaaammdd. + +Se pide una acción `clearRecentHistory` que elimina todas las páginas web del historial que tienen último acceso posterior a una fecha dada. + + +```alg +type + tWebPage = record + id: integer; + address: integer; + lastAccess: integer; + end record + + tHistory = tStack(tWebPage); +end type + +action clearRecentHistory(inout s: tHistory, in date: integer) + var + end: boolean; + p: tWebPage; + end var + + end:= false; + + while not isEmptyStack(s) and not end do + top(s, p); + + if p.lastAccess > date then + pop(s); + else + end:= true; + end if + end while +end action +``` + +```c +#include +#include + +typedef struct { + int id; + int address; + int lastAccess; +} tWebPage; + +typedef struct { + tWebPage A[MAX]; + int nelem; +} tHistory; + +void clearRecentHistory(tHistory *s, int date) { + bool end; + tWebPage p; + + end = false; + + while (!isEmptyStack(*s) && !end) { + top(*s, &p); + + if (p.lastAccess > date) { + pop(s); + } else { + end = true; + } + } +} +``` + + +#### 19.1.3 Ejemplo 3 + +Continuando con el ejercicio anterior del historial de accesos de páginas web, ahora vamos a diseñar una acción `pushupWebPage` que recarga el acceso a una página dada. Esto significa que sube su posición en la pila hasta la cima, para pasar a ser la de acceso más reciente. + +```alg +action pushupWebPage(inout s: tHistory, in id: integer); + var + aux: tHistory; + found: boolean; + tmp, p: tWebPage; + end var + + found:= false; + initStack(aux); + + while not isEmptyStack(s) and not found do + top(s, p); + + if p.id = id then + pop(s); + found:= true; + else + pop(s); + push(aux, p); + end if + end while + + while not isEmptyStack(aux) do + top(aux, tmp); + push(s, tmp); + pop(aux); + end while + + if found = true then + push(s, p); + end if + +end action +``` + +```c +#include +#include + +void pushupWebPage(tHistory *s, int id) { + tStack aux; + bool found; + + tWebPage tmp, p; + found = false; + initStack(&aux); + + while (!isEmptyStack(*s) && !found) { + top(*s, &p); + + if (p.id == id) { + pop(s); + found = true; + } else { + pop(s); + push(&aux, p); + } + } + + while (!isEmptyStack(aux)) { + top(aux, &tmp); + push(s, tmp); + pop(&aux); + } + + if (found) { + push(s, p); + } +} +``` ### 19.2. Ejemplos sobre el TAD cola +#### 19.2.1 Ejemplo 1 + +Se modela la cola de trabajos de una impresora. Los trabajos nuevos se insertan al final de la secuencia de trabajos y el primer trabajo de la secuencia es el primero en enviarse a imprimir. + +Cada trabajo tiene la siguiente información: +- Un identificador único de tipo entero. +- La dirección donde se encuentra almacenado el trabajo a imprimir. +- el tamaño en bytes. +- El tipo de impresión (a doble o a simple cara) + +Se debe diseñar una función `countBig` que devuelva el número de trabajos de la cola de impresión cuyo tamaño es superior a un límite dado en bytes. + +```alg +type + tWork = record + id: integer; + address: integer; + size: integer; + doubleSide: boolean; + end record + + tPrintQueue = tQueue(tWork); +end type + +function countBig(q: tPrintQueue, limit: integer): integer + var + w: tWork; + ini, count: integer; + end: boolean; + end var + + count := 0; + + if not is EmptyQueue(q) then + head(q, w); + ini := w.id; + + if w.size > limit then + count := count + 1; + end if + + dequeue(q); + enqueue(q, w); + end:= false; + + while not end do + head(q, w); + + if w.id = ini then + end:= true; + else + if w.size > limit then + count := count + 1; + end if + + dequeue(q); + enqueue(q, w); + end if + end while + end if + + return count; + +end function +``` + +```c +#include +#include + +// Define la estructura de un trabajo de impresión +typedef struct { + int id; // Identificador del trabajo + int address; // Dirección del trabajo + int size; // Tamaño del trabajo + bool doubleSide; // Indica si el trabajo es a doble cara +} tWork; + +// Define la estructura de una cola de impresión +typedef struct { + tWork A[MAX]; // Array de trabajos + int nelem; // Número de elementos en la cola +} tPrintQueue; + +typedef tWork elem; +typedef tPrintQueue tQueue; + +// Función para contar cuántos trabajos en la cola son más grandes que un cierto límite +int countBig(tQueue q, int limit){ + elem w; // Elemento actual + int ini; // Identificador del primer elemento + int count; // Contador de trabajos grandes + bool end; // Indicador de fin de la cola + count=0; + + // Si la cola no está vacía + if(!isEmptyQueue(q)){ + head(q, &w); // Obtiene el primer elemento + ini=w.id; // Guarda el identificador del primer elemento + // Si el tamaño del trabajo es mayor que el límite + if(w.size>limit){ + count++; // Incrementa el contador + } + dequeue(&q); // Elimina el primer elemento de la cola + enqueue(&q, w); // Añade el primer elemento al final de la cola + end=false; + // Mientras no se haya vuelto al primer elemento + while(!end){ + head(q, &w); // Obtiene el primer elemento + // Si se ha vuelto al primer elemento + if(w.id==ini){ + end=true; // Indica el fin de la cola + }else{ + // Si el tamaño del trabajo es mayor que el límite + if(w.size>limit){ + count++; // Incrementa el contador + } + dequeue(&q); // Elimina el primer elemento de la cola + enqueue(&q, w); // Añade el primer elemento al final de la cola + } + } + } + return count; // Devuelve el contador +} +``` + + +#### 19.2.2 Ejemplo 2 + +Siguiendo con el ejercicio anterior. Se debe diseñar una acción `concatQueues` que, dadas dos colas de trabajos, concatena la segunda cola con la primer y vacía la segunda cola de trabajos. Si la primera cola de trabajos está llena, los trabajos que no se hayan concatenado se quedarán en la segunda cola y el parámetro de salida `completed` será falso. Si se puede realizar la concatenación, el parámetro de salida `completed` será verdadero. + +```alg +action concatQueues(inout q1, q2: tPrintQueue, out completed: boolean) + var + w: tWork; + end var + + while not isEmptyQueue(q2) and not isFullQueue(q1) do + head(q2, w); + enqueue(q1, w); + dequeue(q2); + end while + + completed := isEmptyQueue(q2); +end action +``` + +```c +#include +#include + +void concatQueues(tQueue *q1, tQueue *q2, bool *completed){ + tWork w; // Elemento actual + + while (!isEmptyQueue(*q2) && !isFullQueue(*q1)){ + head(*q2, &w); // Obtiene el primer elemento + enqueue(q1, w); // Añade el primer elemento a la cola 1 + dequeue(q2); // Elimina el primer elemento de la cola 2 + } + *completed = isEmptyQueue(*q2); // Indica si se han añadido todos los elementos +} +``` + + +#### 19.2.3 Ejemplo 3 + +Siguiendo con el ejercicio anterior. Se debe diseñar una acción `serveDoubleSide`, que envía a imprimir todos los trabajos cuyo tipo de impresión es doble cara. Se devuelve en el parámetro qsingle, una cola creada con todos los trabajos no enviados a imprimir, es decir, los que son de tipo simple cara. + +Para enviar un trabajo a imprimir se dispone de la acción `printWork`, que recibe el parámetro el trabajo a imprimir con todos sus datos (identificador, dirección, tamaño y tipo de impresión). + +```alg +action serveDoubleSide(inout q: tPrintQeueu, inout qsingle: tPrintQueue) + var + w: tWork; + end var + + initQueue(qsingle); + + while not isEmptyQueue(q) do + head(q, w); + + if w.doubleSide then + printWork(w); + else + enqueue(qsingle, w); + end if + + dequeue(q); + + end while +end action +``` + +```c +#include +#include + +void serveDoubleSide (tQueue *q, tQueue *qSingle){ + elem w; + initQueue(qSingle); + + while (!isEmptyQueue(*q)){ + head(*q, &w); + + if (w.doubleSide){ + printWork(w); + } else { + enqueue(qSingle, w); + } + + dequeue(q); + + } +} +``` + ### 19.3. Ejemplos sobre el TAD lista +#### 19.3.1 Ejemplo 1 +Se modela la lista de asientos disponibles de un vuelo concreto mediante una lista de asientos. Cada asiento tiene la siguiente información: +- Número entero que identifica +- Tipo de asiento: + - Regular + - Puerta de emergencia - No se asignan a menores de 12 años. + +De cada pasajero se conoce: +- Identificador de tipo entero +- Número de maletas que ha facturado +- Edad + +Se debe diseñar una acción `seatAssign` que, dada una lista de asientos disponibles y la información de un pasajero, devuelve el asiento que se le ha asignado y el parámetro `found` con valor verdadero. Si no ha sido posible asignarle un asiento, el parámetro `found` será falso. + +```alg +type + tPerson = record + id: integer; + bags: integer; + age: integer; + end record + + tSeatType = {REGULAR, EMERGENCY_DOOR} + + tSeat = record + num: integer; + type: tSeatType; + end record + + tListSeat = tList(tSeat); +end type + +action seatAssing(inout l: tListSeat, in p: tPerson, inout s: tSeat, inout found: boolean) + var + pos: integer; + found: boolean; + end var + + pos := 1; + found := false; + + while pos ≤ lengthList(l) and not found do + get(l, pos, s); + + if p.age > 12 or (p.age ≤ 12 and s.type ≠ EMERGENCY_DOOR) then + delete(l, pos); + found := true; + else + pos := pos + 1; + end if + end while +end action +``` + +```c +#include +#include + +typedef struct { + int id; + int bags; + int age; +} tPerson; + +typedef enum {REGULAR, EMERGENCY_DOOR} tSeatType; + +typedef struct { + int num; + tSeatType type; +} tSeat; + +typedef struct { + tSeat A[MAX]; + int nelem; +} tListSeat; + +typedef tSeat elem; +typedef tListSeat tList; + +void seatAssign(tListSeats *l, tPerson p, tSeat *s, bool *found){ + int pos; + pos = 0 + *found = false; + + while (pos < lengthList(*l) && !*found){ + get(*l, pos, s); + + if (p.age > 12 || (p.age <= 12 && s.type != EMERGENCY_DOOR)){ + delete(l, pos); + *found = true; + } else { + pos++; + } + } +} +``` + + +#### 19.3.2 Ejemplo 2 + +Se modela un conjunto de facturas de una empresa mediante una lista. Las facturas no siguen un orden especial en el alta del conjunto y es posible dar de baja cualquier factura del conjunto. + +De cada factura se conoce: +- Un identificador de tipo entero +- Cantidad de dinero de tipo real +- Fecha de la factura en formato aaaammdd + +Se debe diseñar una función `countSmallAmounts` que devuelva el número de facturas con cantidad de dinero menor a una cantidad dada. + +```alg +type + tInvoice = record + id: integer; + amount: real; + date: integer; + end record + + tInvoiceList = tList(tInvoice); +end type + +function countSmallAmounts(l: tInvoiceList, a: real): integer + var + inv: tInvoice; + total: integer; + pos: integer; + end var + + pos := 1; + total := 0; + + while pos ≤ lengthList(l) do + get(l, pos, inv); + + if inv.amount < a then + total := total + 1; + end if + + pos := pos + 1; + end while + + return total + +end function +``` + +```c +#include + +typedef struct { + int id; + float amount; + int date; /* aaaammdd */ +} tInvoice; + +typedef struct { + tInvoice A[MAX]; + int nelem; +} tInvoiceList; + +typedef tInvoice elem; +typedef tInvoiceList tList; + +int countSmallAmounts(tList l, float a){ + tInvoce inv; + int total; + int pos; + pos = 0; + total = 0; + + while (pos < lengthList(l)){ + get(l, pos, &inv); + + if (inv.amount < a){ + total++; + } + pos++; + } + return total; +} +``` + + +#### 19.3.3 Ejemplo 3 + +Siguiendo el ejercicio anterior, vamos a diseñar la acción `findInvoice` que, dado el identificador de una factura, devuelve el parámetro `found` verdadero si la factura está en el conjunto de facturas así como su posición en la lista. En caso contrario, la acción devuelve el parámetro found con valor falso. + +```alg +action findInvoice(in l: tInvoice, in d: integer, out found: boolean, out pos:integer) + var + inv: tInvoice; + end var + + found := false; + pos := 1; + + while pos ≤ lengthList(l) and not found do + get(l, pos, inv); + + if inv.id = d then + found := true; + else + pos := pos + 1; + end if + end while +end action +``` + +```c +#include +#include + +void findInvoice(tInvoices l, int id, bool *found, int *pos){ + tInvoice inv; + *found = false; + *pos = 0; + + while (*pos < lengthList(l) && ! (*found)){ + get(l, *pos, &inv); + + if (inv.id == id){ + *found = true; + } else { + *pos++; + } + } +} +``` [Volver arriba](#pec-6)