Sunteți pe pagina 1din 10

TEMA 4 TIPOS DE DATOS ABSTRACTOS

DINÁMICOS LINEALES

PILAS
Como idea intuitiva podríamos verlo como una pila de libros de la cual solo
podemos coger o mirar el primero.
No se podrá acceder a los elementos anteriores al último (cima). Una pila es
una estructura LIFO (ultimo en entrar primero en salir).
Es tu TDA lineal y dinámico (el número de elementos no es fijo). Sin
embargo su implementación puede ser dinámica, a través de punteros, o estática,
mediante arreglos.
Las operaciones básicas asociadas al TDA son:
- PilaVacía
- PilaLlena
- CrearPila
- Apilar
- esafilar
- ConsultarCima

Implementaciones estáticas

Definición del tipo PILA


CONST
MaxPila= …;
TYPE
TipoPila=RECORD
datos : ARRAY[1..MaxPila] OF TipoDatos;
cima : INTEGER;
END;

PROCEDURE InicioPila(VAR pila:TipoPila);


BEGIN
pila.cima := 0;
END InicioPila;

PROCEDURE PilaVacia (pila:TipoPila):BOOLEAN;


BEGIN
RETURN pila.cima := 0;
END PilaVacia;
PROCEDURE Apilar(VAR pila:TipoPila; dato:TipoDatos);
BEGIN
INC(pila.cima);
pila.datos[pila.cima] := dato;
END Apilar;

PROCEDURE Desapilar(VAR pila:TipoPila; VAR dato:TipoDatos);


BEGIN
dato := pila.datos[pila.cima];
DEC(pila.cima);
END Desapilar;

PROCEDURE ConsultarPila(pila:TipoPila; VAR dato:TipoDatos);


BEGIN
dato := pila.datos[pila.cima];
END ConsultarPila;

Implementaciones dinámicas

Definición del tipo PILA


TYPE
pila= POINTER TO NodoPila
NodoPila=RECORD
datos : TipoDatos;
enlace : tipopila
END;

PROCEDURE InicioPila(VAR pila:TipoPila);


BEGIN
pila := NIL
END InicioPila;

PROCEDURE PilaVacia (pila:TipoPila):BOOLEAN;


BEGIN
RETURN pila := NIL;
END PilaVacia;

PROCEDURE PilaLlena (pila:TipoPila):BOOLEAN;


BEGIN
RETURN NOT Available(Size(NodoPila));
END PilaLlena;
PROCEDURE Apilar(VAR pila:TipoPila; dato:TipoDatos);
VAR aux: pila;
BEGIN
NEW(aux);
aux^.datos := dato;
aux^.enlace := pila;
pila:=aux;
END apilar;

PROCEDURE Desapilar(VAR pila:TipoPila; VAR dato:TipoDatos);


VAR aux: pila;
BEGIN
NEW(aux);
dato := pila^.dato;
aux := pila;
pila := pila^.enlace;
DISPOSE (aux);
END Desapilar;

PROCEDURE ConsultarPila(pila:TipoPila; VAR dato:TipoDatos);


BEGIN
dato := pila^.datos;
END ConsultarPila;

Imprimir una pila:


PROCEDURE ImprimirPila(pila:TipoPila);
VAR dato:TipoDatos;
BEGIN
WHILE NOT PilaVacia DO
Desapilar(pila,dato);
Imprimir(dato);
END;
END ImprimirPila;
COLAS
Es tu TDA lineal y dinámico (el número de elementos no es fijo), aunque la
implementación puede ser dinámica, a través de punteros, o estática, mediante
arreglos.
Es una estructura en la que los elementos se añaden por un extremo,
denominado final de cola, pero en este caso se suprimen por el otro lado, conocido
como principio de cola. Una pila es una estructura FIFO (primero en entrar primero
en salir).
Las operaciones básicas asociadas a este TDA son:
- Iniciarcola
- ColaVacia
- ColaLLena
- Encolar
- Desencolar
- ConsultarFrente
- ConsultarFinal

Nota de la implementación estática:


Lo implementaremos en un Arreglo circular ya que si no según avanzáramos
acabaríamos enseguida el arreglo. Supongamos el siguiente arreglo:
final frente
5 7 6 25 41 25 6 9

Esta cola estaría llena ya que frente=final+1


Pero si pensamos ahora en el siguiente vector:
frente
final
5 7 6 25 41 25 6 9
Tenemos una cola de un elemento, si avanzamos el frente tendríamos una
cola vacía pero la expresión frente=final+1; la misma expresión que para la cola
llena. Para solventar este problema hacemos que el frente no apunte al primer
elemento sino a la posición anterior. Así una cola llena quedaría
final frente
5 7 6 ----- 41 25 6 9
frente =final+1
Y una cola vacia:
final
frente
5 7 6 ----- 41 25 6 9
frente = final
Implementaciones estáticas

Definición del tipo COLA

CONST
MAXCOLA=…+1; (* perdemos una posición por la operación antes explicada *)
TYPE
TipoIndice:[1..MAXCOLA];
TipoCola = RECORD
datos : ARRAY TipoIndice OF TipoDatos;
frente,fina : TipoIndice;
END;

PROCEDURE InicioCola(VAR cola:Tipocola);


BEGIN
cola.frente := MAXCOLA;
cola.final := cola.frente;
END InicioCola;

PROCEDURE ColaVacia (cola:TipoCola):BOOLEAN;


BEGIN
RETURN cola.frente := cola.final;
END ColaVacia;

PROCEDURE Encolar(VAR cola:TipoCola; dato:TipoDatos);


BEGIN
IF cola.fi=MAXCOLA THEN
cola.final := 1;
ELSE
INC(cola.final);
END;
cola.datos[cola.final] := dato;
END Encolar;

PROCEDURE Desencolar(VAR cola:TipoCola; VAR dato:TipoDatos);


BEGIN
IF cola.frente=MAXCOLA THEN
cola.frente := 1;
ELSE
INC(cola.frente);
END;
Dato := cola.datos[cola.final];
END Desencolar;
PROCEDURE Consultarfrente(cola:TipoCola; VAR dato:TipoDatos);
BEGIN
IF cola.frente=MAXCOLA THEN
dato := cola.datos[1];
ELSE
dato := cola.datos[cola.frente+1];
END;
END Consultarfrente;

PROCEDURE ConsultarFinal(cola:TipoCola; VAR dato:TipoDatos);


BEGIN
dato := cola.datos[cola.final];
END ConsultarFinal;

Implementaciones dinámicas

Definición del tipo PILA


TYPE
Ptr_nodo=POINTER TO NodoCola
NodoCola= RECORD
Datos : TipoDatos;
enlace : Ptr_Nodo;
END;
TipoCola= RECORD
final,frente : Ptr_Nodo;
END;

PROCEDURE InicioCola(VAR cola:Tipocola);


BEGIN
cola.final := NIL;
END InicioCola;

PROCEDURE ColaVacia (cola:TipoCola):BOOLEAN;


BEGIN
RETURN cola.final := NIL;
END ColaVacia;

PROCEDURE Consultarfrente(cola:TipoCola; VAR dato:TipoDatos);


BEGIN
dato := cola.frente^.datos;
END Consultarfrente;
PROCEDURE ConsultarFinal(cola:TipoCola; VAR dato:TipoDatos);
BEGIN
dato := cola.fi^.datos;
END ConsultarFinal;

PROCEDURE Desencolar(VAR cola:TipoCola; VAR dato:TipoDatos);


VAR
aux:Ptr : Nodo;
BEGIN
NEW(aux);
dato := cola.frente^.datos;
aux:=cola^.frente;
cola.frente:=Cola.frente^.enlace;
DISPOSE(aux);
END Desencolar;

PROCEDURE Encolar(VAR cola:TipoCola; dato:TipoDatos);


BEGIN
NEW(aux);
aux^.datos := dato;
aux^.enalce := cola.frente;
IF ColaVacia(Cola) THEN
Cola.frente := aux;
ELSE
Cola.final^.enlace := aux;
END
cola.final := aux;
END Encolar;
CONSIDERACIONES GENERALES
Antes de finalizar es conveniente realizar algunas consideraciones
generales con el fin de ver con una perspectiva de conjunto los conceptos
presentados en este tema y relacionarlos con otros ya conocidos.
1. A lo largo del rema se han ido analizando diversos TDA y diferentes
algoritmos. En ocasiones, se han encontrado soluciones que no son
adecuadas aunque ‘funcionen’. Debemos insistir en este punto. No todo lo
que funciona es la solución correcta. Recordemos el ejemplo de la
implementación de la cola mediante una estructura dinámica. Una lista
doblemente enlazada no es adecuada por su innecesario coste de
almacenamiento y procesamiento (pero funciona), y una lista lineal con un
único puntero externo tampoco es adecuada por su coste computacional
(aunque también funciona).
La conclusión es clara: la lista enlazada con dos punteros externos
(frente y final) es la solución adecuada. La idoneidad de una solución vendrá
dada, como siempre, por los criterios de economía de almacenaje y coste
computacional.
2. En el Capitulo I ya se definió un TDA dinámico como aquel en el que
el número de elementos que lo componen es variable. En este capítulo se han
definido las pilas y las colas y se ha comentado su naturaleza dinámica.
Posteriormente se ha estudiado su implementación estática y dinámica. Por
otro lado, una definición alternativa (y también correcta) de una estructura
dinámica es la siguiente: aquella estructura que puede aumentarse o
reducirse en tiempo de ejecución. ¿Es esto contradictorio con el hecho de
que una pila, por ejemplo, implementada mediante arreglos sea dinámica? La
respuesta es claramente no. Los tipos de datos abstractos son estáticos o
dinámicos dependiendo exclusivamente de si definición, nunca de su
implementación. Así en la implementación estática no debe confundirse la
pila con el arreglo. El arreglo no crece ni disminuye en tiempo de ejecución,
pero la pila sí. Las componentes del arreglo que no han sido ocupada
introduciendo datos en la pila no forman parte de ésta. Tan dinámica es una
pila implementada mediante arreglos como una implementada con una lista
enlazada.
3. Las implementaciones estáticas tienen la ventaja de que son más
rápidas. El manejo de arreglos utilizando el índice es más rápido que la
realización del conjunto de manipulaciones de punteros que son necesarias
en las estructuras basadas en variables dinámicas.
4. La clave fundamental para decidir entre las implementaciones
estáticas y dinámicas de tipos de datos abstractos dinámicos radica en el
hecho de si se conoce o no a priori un número máximo de datos que deberán
ser almacenados. Si este número máximo es conocido, la implementación
estática no es sólo posible sino que es más adecuada atendiendo a las
consideraciones expuestas en el punto 3. Como ejemplo ilustrativo
considérese el ejemplo de la evaluación de la parentización de una expresión
en notación infija.
Al solucionar este ejercicio de programación se llegó a la conclusión
de que l TDA adecuado es la pila. La siguiente cuestión a resolver era si
debe ser estática o dinámica. En el primer caso (a) la especificación decía
que las cadenas se leían desde la consola. Si las consolas tienen un buffer
que acepta un número limitado de caracteres, éste puede ser conocido a
priori. Por tanto, es posible una implementación estática y atendiendo a las
consideraciones realizadas en el punto 3 es más adecuada. Sin embardo, en
el segundo caso (b) las cadenas debían leerse desde un fichero. Entonces no
podemos conocer a priori un número máximo de caracteres que tienen que
almacenarse. Por tanto, debería utilizarse una implementación dinámica.
5. También debe tenerse en cuenta que la utilización de un TDA
dinámico para resolver un problema en el que el número de elementos a
almacenar es variable no es siempre adecuada. Supongamos, por ejemplo,
que se necesita almacenar un apellido en una estructura de datos, y que las
operaciones a realizar con él descartan la utilización de una pila o una cola.
Tendremos que considerar un arreglo o una lista enlazada. Como los
apellidos tienen diferente cantidad caracteres podría pensarse que su
almacenamiento en una lista enlazada es más adecuado que en un arreglo.
Sin embargo, esto no es cierto. Es verdad que el número de elementos es
variable, pero por otro lado sabemos que el número de caracteres es
limitado (¿Cuántos apellidos tienen más de quince caracteres?, ¿o más de
veinticinco?). Si se utilizase una lista enlazada para cada apellido se
ahorraría la memoria de las posiciones en las que no hay caracteres. Pero
también debe tenerse en cuenta que se gastaría memoria para almacenar los
punteros de los nodos (y un carácter ocupa menos que una dirección) y
además su manejo es más lento como se indicó en 3. Por tanto, la solución
con un arreglo es la adecuada.
6. Por último, es muy importante realizar una consideración acerca de la
implementación Vaciar una pila o una cola que contiene elementos, Como
Vaciar no es un operador básico hay que implementarla en función de los
operadores básicos de la misma. En sentido estricto hay que sacar todos los
elementos hasta que la pila o la cola esté vacía. Si se piensa en las
implementaciones con arreglos Vaciar puede implementarse mediante una
simple llamada a Iniciar, ya que los demás antiguos elementos del arreglo
serán “basura” cuando se haya iniciado. Sin embargo, hacer esto con una
implantación dinámica tiene resultados catastróficos puesto que la lista
enlazada sobre la que se implementó la pila o la cola no ha sido liberada,
ocupa memoria y no estará ya accesible para liberarla. Éste es un error muy
grave. Entonces, ¿hay que considerar la implementación para realizar las
funciones sobre los TDA? Es decir, por un lado tendremos que
desarrollarlas en función de los operadores básicos y auxiliares definidos
sobre ellas, pero por otro lado Vaciar sacando todos los elementos con una
implementación estática tiene un coste computacional innecesario. La
solución en sentido estricto es realizar la función sin atender a los detalles
de implementación, aunque el coste computacional sea innecesario. Sin
embargo, observando el hecho, una solución elegante es definir un nuevo
TDA en el que se incorpore la operación Vaciar entre las básicas, llamándola
por ejemplo Pila con vaciado, o Cola con vaciado. No debe nunca olvidarse
que los TDA se definen de forma estricta pero siempre está permitido
definir cualquier TDA que sea necesario. Lo importante es evitar
ambigüedades.

CONCLUSIONES
En conclusión, no existen reglas ni para decidir de forma absoluta entre los
TDA dinámicos o estáticos ni tampoco para concluir que una implementación
dinámica es siempre mejor que una estática para las estructuras dinámicas. Sin
embargo, sí se dispone de criterios claros para adoptar la solución adecuada al
resolver un problema concreto. Estos criterios son la economía de almacenamiento
y el coste computacional. Como sucedía en los algoritmos de clasificación se deben
conocer todas las posibilidades, en este caso los TDA y sus posibles
implementaciones, y al resolver unas especificaciones dadas elegir razonablemente
las más adecuadas.
En este capítulo se han presentado los TDA dinámicos lineales
fundamentales para la organización de datos, que mantienen una organización
secuencial con un predecesor y su sucesor. En primer lugar se han introducido,
como ejemplos de construcción de TDA dinámicos, algunos similares a las listas
enlazadas. Seguidamente se han estudiado detalladamente los dos TDA dinámicos
lineales más característicos: las pilas y las colas, desarrollando completamente sus
implementaciones, tanto estáticas como dinámicas, y se han explorado las diversas
posibilidades hasta llegar a una implementación determinada satisfactoria. Como es
habitual, en ocasiones se han encontrado soluciones que no son adecuadas aunque
‘funciones’.
Debe observarse que se ha insistido en el hecho, bien conocido y detallado
desde el comienzo del texto, de que el carácter estático o dinámico del TDA es
independiente de su implementación. Las implementaciones estáticas tienen la
ventaja de que son más rápidas, mientras que las dinámicas permiten un mejor
aprovechamiento de la memoria. La clave fundamental para decidir entre las
implementaciones estáticas y dinámicas de tipos de datos abstractos dinámicos
radica en el hecho de si se conoce o no a priori un número máximo de datos que
deberán ser almacenados. Sin embargo, esto no significa que la utilización de un
TDA dinámico para resolver un problema en el que el número de elementos a
almacenar es variable no es siempre adecuada.
En conclusión, no existen reglas ni para decidir para todos los casos que un
TDA es más adecuado ni para considerar únicamente una implementación
determinada. El estudio de los conceptos desarrollados en este capítulo permite
disponer de criterios claros para adoptar la solución adecuada al resolver un
problema concreto.