Update PEC6

This commit is contained in:
Manuel Vergara 2024-06-21 21:02:20 +02:00
parent 7a8e9b99f4
commit 9e79d0ffcd

View File

@ -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. <br> `pop()`: Elimina y retorna el elemento del tope de la pila. <br> `isEmpty()`: Verifica si la pila está vacía. <br> `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. <br> `dequeue()`: Elimina y retorna el elemento al frente de la cola. <br> `isEmpty()`: Verifica si la cola está vacía. <br> `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. <br> `eliminar(elemento)`: Elimina un elemento de la lista. <br> `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. <br> - 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. <br> - 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. <br> - Búsqueda es O(n) en una lista simple enlazada sin optimizaciones adicionales. |
| **Uso Típico** | - Manejo de historial de llamadas (undo/redo). <br> - Evaluación de expresiones matemáticas (notación polaca inversa). | - Gestión de tareas en una cola (por ejemplo, impresión en una impresora). <br> - 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. <br> - 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 <stdio.h>
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 <stdio.h>
#include <stdbool.h>
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 <stdio.h>
#include <stdbool.h>
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 <stdio.h>
#include <stdbool.h>
// 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 <stdio.h>
#include <stdbool.h>
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 <stdio.h>
#include <stdbool.h>
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 <stdio.h>
#include <stdbool.h>
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 <stdio.h>
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 <stdio.h>
#include <stdbool.h>
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)