• No se han encontrado resultados

TEMA 3: TIPOS ABSTRACTOS DE DATOS. TIPOS LINEALES

N/A
N/A
Protected

Academic year: 2022

Share "TEMA 3: TIPOS ABSTRACTOS DE DATOS. TIPOS LINEALES"

Copied!
23
0
0

Texto completo

(1)

TEMA 3: TIPOS ABSTRACTOS DE DATOS. TIPOS LINEALES

Í

NDICE

.

3.1 Tipos Abstractos de Datos (TAD´s)

 Especificación e Implementación

 Programación con TADs

 Ejemplo: Números complejos

3.2 Definición y especificación de tipos lineales

 Pilas

 Colas

 Listas con punto de interés

3.3 Implementaciones de los tipos lineales

 Asignación dinámica de memoria. Punteros

 Implementaciones del tipo Pila

 Implementaciones del tipo Cola

 Implementaciones del tipo Lista con punto de interés

O

BJETIVOS

.

 Reforzar a través de la definición de abstracción de datos las diferencias entre especificación (TAD) e implementación de una abstracción.

 Estudiar los tipos (abstractos) de datos lineales: listas, pilas y colas. Analizar sus distintas implementaciones (representación de los datos y diseño de los algoritmos de manejo).

 Estudiar los mecanismos de asignación dinámica de memoria y el tipo puntero.

 Estudiar algunas aplicaciones concretas de estas estructuras de datos.

B

IBLIOGRAFÍA

.

 Aho, Hopcroft, Ullman. “Estructuras de Datos y Algoritmos.”

Addison Wesley Iberoamericana, 1988 (edición en inglés de 1983).

Capítulo1, capítulo 2 (apartados 1, 2, 3 y 4).

 Franch X. “Estructura de Dades. Especificació, disseny i implementació”. Edicions UPC, 1993. Capítulos 3 y 7.

(2)

3.1 T

IPOS

A

BSTRACTOS DE

D

ATOS

(TAD

S

).

Un grupo de operaciones que actúan sobre un mismo tipo de datos (conjunto de valores)

Con la restricción de que el comportamiento de estas operaciones define al tipo.

“Lo interesante de un tipo de datos NO es tanto los valores que lo constituyen y MUCHO MENOS su representación concreta, como la clase de manipulaciones a las que se le puede someter o las propiedades que

satisface” (F. Orejas).

Ejemplos de abstracción de datos:

1.- Tipos elementales de un lenguaje de programación. La definición de la abstracción de datos entero sería:

entero

+ -

*

IMPLEMENTACION

2.- Los tipos definidos por el usuario no tienen operadores predefinidos. Se deben definir como (sub)algoritmos. Definición de la abstracción de datos conjunto:

conjunto

U

-

IMPLEMENTACION

U

E

SPECIFICACIÓN E IMPLEMENTACIÓN DE

TAD

S Como ocurre con toda abstracción, tanto de operaciones como de datos, debemos distinguir dos niveles en su DISEÑO:

1.- Especificación o definición ¿qué es? o ¿qué hace? la abstracción. Esta definición puede ser formal o informal.

Ejemplo: Especificación de una abstracción de operaciones mediante su pre/postcondición expresadas mediante el lenguaje lógico de primer orden.

2.- Implementación o ¿Cómo es? o ¿Cómo lo hace? en un determinado entorno informático.

Ejemplo: Implementación de una abstracción de operaciones mediante las “function” y “procedure” de los lenguajes de programación.

La ESPECIFICACIÓN DE UN TIPO DE DATOS debe incluir:

 Nombre del tipo [parámetro1, ... , parámetron]

 Lista de operaciones sobre el tipo: sintaxis o perfil de las operaciones. Se dará como cabecera de procedimiento o función

 Semántica de las operaciones sobre el tipo: precondición y postcondición

(3)

P

ROGRAMACIÓN CON

TAD

S

VENTAJAS:

 Independencia de la implementación del tipo.

 Programas más portables, legibles, reutilizables.

 Mayor seguridad de la información, menos efectos

colaterales.

 Mayor facilidad para comprobar la corrección.

TADs y Lenguajes de Programación:

 Los lenguajes de programación posteriores al 1975 soportan el concepto de TAD (Ej. ADA, Modula-2, C++)

 Si el lenguaje no permite el diseño basado en TADs será necesario suplir esta insuficiencia con una disciplina en su uso: usar sólo las posibilidades del mismo que no violen los principios de ocultamiento establecidos en el diseño (Ej.

PASCAL, C, FORTRAN, COBOL)

E

JEMPLO

:

NÚMEROS COMPLEJOS

Para poder resolver problemas en los que se utilizan números complejos se define el tad número complejo mediante:

- una representación posible (forma cartesiana) - operaciones de transformación de tipos:

- creaC, crea un complejo a partir de dos reales - prealC, obtiene la parte real de un complejo - pimgC, obtiene la parte imaginaria de un complejo - operaciones aritméticas entre complejos:

- sumaC, restaC, multC, divsnC y módC

Especificación del Tad Número complejo

TIPO complejo SINTAXIS

función creaC(pr,pi:real):complejo;

función prealC(c:complejo):real;

función pimagC(c:complejo):real;

función sumaC(c1,c2:complejo):complejo;

función restaC(c1,c2:complejo):complejo;

función multC(c1,c2:complejo):complejo;

función divsnC(c1,c2:complejo):complejo;

función módC(c:complejo):real;

SEMÁNTICA

(4)

{ pr=PR  pi=PI } c:=creaC(pr,pi) { c=(PR + PIi) } { c = (A + Bi) }

r :=prealC(c) { r = A }

{ c = (A + Bi) } r :=pimagC(c) { r = B }

{ c1 = (A + Bi)  c2 = (C + Di) } c3:=sumaC(c1,c2)

{ c3 =(A+C) + (B+D)i }

{ c1 = (A + Bi)  c2 = (C + Di) } c3:=restaC(c1,c2)

{ c3 =(A-C) + (B-D)i }

{ c1 = (A + Bi)  c2 = (C + Di) } c3:=multC(c1,c2)

{ c3 =(AC-BD) + (AD+BC)i }

{ c1 = (A + Bi)  c2 = (C + Di)  c2  (0 + 0i) } c3:=divsnC(c1,c2)

{ c3 =[(AC+BD)/(C²+D²)] + [(BC-AD) /(C²+D²)] i } { c = (A + Bi) }

r :=módC(c)

{ r = raíz(A²+B²)  0r }

Implementación del Tad Número complejo

Como se sigue la representación cartesiana de los números complejos, se representarán mediante una tupla con dos componentes:

Tipo: complejo = tupla

preal: real;

pimag: real;

ftupla Operaciones:

{ pr=PR  pi=PI }

función creaC(pr,pi:real): complejo;

var c:complejo fvar c.preal:=pr; c.pimag:=pi;

devuelve(c) ffunción

{ creaC(pr,pi)=(PR + PIi) } { c = (A + Bi) }

función prealC(c:complejo):real;

devuelve(c.preal) ffunción

{ prealC(c)= A } { c = (A + Bi) }

función pimagC(c:complejo):real;

devuelve(c.pimag) ffunción

{ pimagC(c) = B }

{ c1 = (A + Bi)  c2 = (C + Di) }

(5)

función sumaC(c1,c2:complejo):complejo;

var c3:complejo fvar

c3.preal:=c1.preal + c2.preal;

c3.pimag:=c1.pimag + c2.pimag;

devuelve(c) ffunción

{ sumaC(c1,c2) =(A+C) + (B+D)i } { c1 = (A + Bi)  c2 = (C + Di) }

función restaC(c1,c2:complejo):complejo;

var c3:complejo fvar

c3.preal:=c1.preal - c2.preal;

c3.pimag:=c1.pimag - c2.pimag;

devuelve(c) ffunción

{ restaC(c1,c2) =(A+C) - (B+D)i } { c1 = (A + Bi)  c2 = (C + Di) }

función multC(c1,c2:complejo):complejo;

var c3:complejo fvar

c3.preal:=c1.preal*c2.preal - c1.pimag*c2.pimag;

c3.pimag:=c1.preal*c2.pimag + c2.preal*c1.pimag;

devuelve(c) ffunción

{ multC(c1,c2) =(AC-BD) + (AD+BC)i }

{ c1 = (A + Bi)  c2 = (C + Di)  c2  (0 + 0i) } función divsnC(c1,c2:complejo):complejo;

var c3:complejo; aux:real fvar

aux:=c2.preal*c2.preal + c2.pimag*c2.pimag;

c3.preal:=(c1.preal*c2.preal + c1.pimag*c2.pimag)/aux;

c3.pimag:=(c2.preal*c1.pimag - c1.preal*c2.pimag)/aux;

devuelve(c3) ffunción

{divsnC(c1,c2)=[(AC+BD)/(C²+D²)]+[(BC-AD) /(C²+D²)]i}

{ c = (A + Bi) }

función módC(c:complejo):real;

devuelve(raíz(c.preal*c.preal+c.pimag*c.pimag)) { módC(c) = raíz(A²+B²)  0r }

Una vez definido el tad número complejo, se tiene:

- Es posible utilizar el tipo complejo para efectuar nuevas definiciones, de forma similar a como se haría con los restantes tipos numéricos.

- Las únicas operaciones permitidas sobre datos de tipo complejo son las definidas en el tad del mismo.

- Los algoritmos que utilicen el tad complejo no dependen de la representación interna de dicho tipo, ni de la implementación que se haya efectuado de las operaciones.

(6)

Ejemplo: utilización del tad complejo

En teoría de circuitos es habitual utilizar matrices de números complejos para representar circuitos eléctricos en los que aparecen elementos inductivos o capacitivos.

En el estudio de dichos circuitos se utilizan operaciones algebraicas como el producto matricial, entre otras, así:

dadas A,BCnxn ;se desea calcular MCnxn, tal que M=A*B Tipo

MatrizC : Vector[1..N,1..N] de complejo;

{a=A  b=B }

procedimiento prodMC( a,b: MatrizC; var m: MatrizC);

var i,j,k: entero fvar Para i:=1 hasta N hacer

Para j:=1 hasta N hacer

m[i,j]:=creaC(0,0); /* se asigna el neutro*/

Para k:=1 hasta N hacer

m[i,j]:=sumaC(m[i,j], multC(a[i,k],b[k,j]));

fpara fpara fpara

fprocedimiento { m= A*B }

3.2 D

EFINICIÓN Y

E

SPECIFICACIÓN DE

T

IPOS

L

INEALES

PILAS

Una abstracción de datos Pila es una estructura LIFO (del inglés Last-In First-Out, o "el último que entra es el primero que sale"):



Los elementos se insertan de uno en uno y se recuperan también de uno en uno, en orden inverso a como se han introducido.

Se caracteriza por las siguientes operaciones:

creap(p): crea una pila vacía

apilar(p,v): añade el elemento v en la pila p desapilar(p): saca un elemento de la pila topep(p): consultar el tope de la pila vaciap?(p): decidir si la pila está vacia

Ejemplos: utilización de pilas en el diseño de algoritmos

 Gestión de las direcciones de retorno que hace el sistema en las llamadas a procedimientos en un programa.

 Evaluación de expresiones.

 Transformación de algunas clases de funciones recursivas en iterativas.

(7)

Especificación de PILA o TAD PILA

TIPO pila [elemento]

SINTAXIS

procedimiento creap(sal p:pila);

procedimiento apilar(ent/sal p:pila; v:elemento);

procedimiento desapilar(ent/sal p:pila);

función topep(p:pila) devuelve elemento;

función vaciap?(p:pila) devuelve lógico;

SEMÁNTICA {}

creap(p) {p= }

{p=P1 P2.. Pn v=V } apilar(p,v)

{p=P1 P2.. Pn V}

{p=P1 P2.. Pn p<> } desapilar(p)

{p=P1 P2.. Pn-1}

{p=P1 P2.. Pn p<> } e:=topep(p)

{ p=P1 P2.. Pn e= Pn} { p=P1 P2.. Pn }

b:=vaciap?(p)

{ p=P1 P2.. Pn b=(p= )}

COLAS

Una Cola es una estructura FIFO (First-In First-Out o “el primero que entra es el primero que sale”).

Las inserciones se realizan por un extremo y las extracciones por el otro; el elemento que se puede consultar en todo momento es el que primero se insertó.

Se caracteriza por las siguientes operaciones:

creaq: crea una cola vacía

encolar(q,e): inserta un elemento en la cola

desencolar(q): elimina el primer elemento de la cola primero(q): obtiene el primer elemento de la cola

vaciaq?(q): devuelve CIERTO si la cola está vacía y FALSO en caso contrario

Ejemplo de aplicación:

La asignación de CPU a procesos de usuarios que realiza el sistema operativo con una política round-robin sin prioridades:los procesos que solicitan el procesador se encolan de forma que cuando acaba el que se está ejecutándose actualmente pasa a ejecutarse el que lleva mas tiempo esperando.

(8)

Especificación de COLA o TAD COLA

TIPO cola [elemento]

SINTAXIS

procedimiento creaq (e/s q:cola);

procedimiento encolar (e/s q:cola; e:elmento);

procedimiento desencolar (e/s q:cola);

función primero (q:cola) devuelve elemento;

función vaciaq? (q:cola) devuelve lógico;

SEMÁNTICA {}

creaq(q) {q= }

{q=Q1 Q2 .. Qn  e=E}

encolar(q,e) {q=Q1 Q2 .. Qn E}

{q=Q1 Q2 .. Qn q<> } desencolar(q)

{q=Q2 .. Qn }

{q=Q1 Q2 .. Qn q<> } e:=primero (q)

{ q=Q1 Q2 .. Qn e = Q1 } { q=Q1 Q2 .. Qn }

b:=vaciaq?(q)

{ q=Q1 Q2 .. Qn b=(q= }

3.2.3 L

ISTAS CON PUNTO DE INTERÉS

Una lista es una secuencia de elementos de un tipo dado, ordenados linealmente según la posición que ocupan en la misma.

Una lista con punto de interés es una lista que tiene una posición especial, o distinguida, sobre la que se aplican las distintas operaciones.

La posición distinguida, o punto de interés, se puede cambiar, con lo que es posible operar sobre los distintos elementos de la lista.

La lista con punto de interés se caracteriza por tres tipos de operaciones:

Operaciones para manipular el contenido:

creal(l): crea una nueva lista vacía.

inserta(l,e): inserta el elemento e en la lista l en la posición inmediatamente anterior a la distinguida.

suprime(l): elimina el elemento que está en la posición distinguida de la lista l.

recupera(l): devuelve el elemento que está en la posición distinguida de la lista l.

Operaciones para cambiar el punto de interés:

principio(l): sitúa el punto de interés de la lista l al principio fin(l): sitúa el punto de interés de la lista l al final

siguiente(l): cambia el punto de interés de la lista l de la posición que ocupa a la siguiente.

Operaciones para consultar el estado de la lista:

(9)

esvacial(l): devuelve CIERTO si la lista l está vacía y FALSO en caso contrario.

esfin(l): devuelve CIERTO si el punto de interés de la lista l es posterior al último elemento y FALSO en caso contrario.

Especificación del Tad Lista con punto de interés

TIPO lista[elemento]

SINTAXIS

procedimiento creal (sal l:lista);

procedimiento inserta (ent/sal l:lista; e:elemento);

procedimiento suprime (ent/sal l:lista);

función recupera (l:lista) devuelve elemento;

procedimiento principio (ent/sal l:lista);

procedimiento fin (ent/sal l:lista);

procedimiento siguiente (l:lista);

función esvacial (l:lista) devuelve lógico;

función esfinl (l:lista) devuelve lógico;

SEMÁNTICA Notación utilizada:

- l = L1L2..Ln 0n, donde n=0 equivale a l= , - pos, 1posn+1,es la posición del punto de interés.

{}

creal(l)

{l =  pos=1}

{l =L1L2..Ln pos=i  1posn+1 e=E } inserta(l,e)

{l=L’1L’2...Ln+1(L’1=L1...L’i-1=Li-1)L’i=E(L’i+1=Li...L’n+1=Ln)pos=i+1}

{l =L1L2...Ln pos=i  1posn } suprime(l)

{l=L’1 L’2...Ln-1 (L’1=L1...L’i-1= Li-1)(L’i=Li+1...L’n-1= Ln) pos=i } {l = L1L2...Ln  1posn }

e:=recupera(l)

{l = L1L2...Ln e=Lpos}

{l=L1 L2...Ln  0n}

principio(l)

{l=L1L2...Ln  pos=1}

{l=L1L2...Ln  0n}

fin(l)

{l=L1L2...Ln pos=n+1}

{l=L1L2...Ln 1posn  pos=i}

siguiente(l)

{ l=L1L2...Ln pos=i+1}

{l=L1L2...Ln  0n}

b:=esvacial(l) {b=(n=0)}

{l=L1L2...Ln  0n}

b:=esfin(l) {b=(pos=n+1)}

Ejemplos de utilización del TAD lista con

punto de interés

(10)

1.- Tratamiento de todos los elementos de una lista l:

{ l=L1L2...Ln  0n }

procedimiento recorrer(ent l:lista) var e:elemento fvar;

primero(l);

mientras no esfin(l) hacer e:=recupera(l); tratar(e);

siguiente(l);

fmientras;

fprocedimiento

{ l=L1L2...Ln  i:1in:tratar(Li) }

Ejercicios propuestos:

-Dada la lista l, crear la lista l’ con el mismo contenido que l -Dada la lista l, borrar todos sus elementos

-Dadas las listas l y l’, obtener l’’ concatenación de l y l’

2.-¿Existe en l un elemento que cumple la propiedad P?

{ l=L1L2...Ln  0n }

función búsqueda(l:lista): lógico var enc: lógico; e:elemento fvar enc:=falso; primero(l);

mientras (no esfinl(l)) y (no enc) hacer e:=recupera(l);

si P(e) entonces enc:=cierto sino siguiente(l) fsi fmientras;

devuelve(enc) ffunción

{ búsqueda(l) l=L1L2...Ln  i:1in:P(Li) }

Ejercicios propuestos:

-Obtener la posición del primer elemento de l que cumple P -Obtener la posición del último elemento de l que cumple P -¿Todos los elementos de l, cumplen la propiedad P?

(11)

3.3 I

MPLEMENTACIÓN DE

T

IPOS

L

INEALES

.

1.- Representación vectorial: Los elementos de la estructura se almacenan en un vector de forma que elementos consecutivos en la pila ocupan posiciones consecutivas en el vector (array).

2.- Representación enlazada: Se rompe la propiedad de representación secuencial y se introduce el concepto de enlace: todo elemento de la estructura identifica explícitamente la posición que ocupa su sucesor en la misma.

La estructura se puede almacenar en un vector o gestionarla dinámicamente utilizando punteros.

A

SIGNACIÓN

D

INÁMICA DE

M

EMORIA

. P

UNTEROS EL TIPO PUNTERO EN PASCAL

Se caracteriza por su bajo nivel de abstracción debido a que esta muy próximo a la organización de la información en la memoria central de un ordenador.

Es un tipo de datos elemental cuyo dominio está compuesto por direcciones de memoria con tipo. El tipo de estas variables se llama Tipo Puntero a un tipo de datos dado T.

No existe un tipo puntero único como ocurre en los otros tipos de datos básicos, sino tipos punteros a tipos de datos.

(12)

Definición de una variable de tipo puntero-a:

VAR nombre_de_variable : T; var

...

I : T;

PtoI : T;

p p+1 p+2 p+n-1

La variable I es de tipo T y la variable PtoI es de tipo puntero a un valor de tipo T que ocupa n posiciones de memoria a partir de la posición p.

Ejemplo: Var I : integer;

PtoI, ptoJ : ˆ integer;

VALOR NULO DE LOS TIPOS PUNTERO-A (polimorfismo) Para todos los conjuntos de valores de los tipos puntero-a, existe un valor que representa la dirección de memoria nula: NIL.

Ejemplo: Var p1: ˆ T1;

p2: ˆ T2; /*Donde T1  T2*/

p1 := Nil;

p2 := Nil;

Los tipos de p1 y p2 son puntero a T1 y puntero a T2. Son dos tipos diferentes a los que se le asigna un valor nulo diferente para cada tipo pero con la misma representación (NIL).

VARIABLES DINÁMICAS

En el lenguaje Pascal existen dos clases de variables según el momento en el que se asigna memoria para poder guardar un valor:

- Una variable es estática si la asignación de memoria se realiza durante el análisis estático (compilación)

- Una variable es dinámica si la asignación de memoria se realiza durante la ejecución del programa.

Para la asignación de memoria a una variable dinámica el programa en ejecución dispone de dos zonas de memoria:

 La Pila: se utiliza para asignar memoria a los parámetros y las variables locales de funciones y procedimientos.

 El Heap: se utiliza para asignar memoria al resto de variables dinámicas.

ASIGNACIÓN Y LIBERACIÓN DE MEMORIA

Las variables dinámicas sólo utilizan memoria durante la ejecución de un programa, por lo que se hace necesario un mecanismo que permita el proceso de asignación y liberación de memoria.

Para la pila de ejecución este proceso es automático en el lenguaje Pascal.

Para la gestión del Heap, Pascal proporciona una primitivas que permiten que el proceso de asignación y liberación de memoria lo determine el propio programador.

(13)

Primitivas para la obtención y liberación de memoria:

A la obtención de memoria para una variable dinámica se denomina CREACIÓN DE UNA VARIABLE DINÁMICA:

Primitiva: NEW(pto)

Var pto : ˆ T;

new(pto);

new(pto) reserva la memoria suficiente en el heap para guardar la representación binaria de un valor de tipo T. En la variable pto se almacena la dirección a la celda de memoria a partir de la cual se puede almacenar un valor de tipo T.

pto

Heap

...

p p+1 p+2 p+n-1 - La variable pto contiene la dirección p

- Son necesarias n celdas para guardar la representación binaria de un valor de tipo T.

A la liberación de la memoria utilizada por una variable dinámica se denomina BORRADO DE UNA VARIABLE DINÁMICA.

Primitiva: DISPOSE(pto)

pto

Heap

...

p p+1 p+2 p+n-1

El procedimiento dispose libera el uso de la memoria del heap utilizada para guardar un valor de tipo T.

dispose(pto) pto

Heap ¿?

...

p

La memoria liberada por dispose queda disponible para nuevas demandas de memoria.

La variable puntero que apunta a una variable dinámica que ha sido borrada pasa a contener un valor indefinido.

(14)

ACCESO A LOS VALORES DE UN VARIABLE DINÁMICA A una variable dinámica se accede a través de la dirección a partir de la cual esta guardado su valor, es decir, utilizando un puntero que la apunta. El operador de acceso a una variable dinámica es: 

Var pto : T;

begin

new(pto);

pto:= <valor>;

Donde <valor> es un dato de tipo T.

Ejemplo:

Program ejemplo (input,output);

Var p:integer;

I:integer;

begin I:=5;

New(p);

p:=I;

writeln(p);

p:= p+1;

writeln(p);

end.

Este programa produce los valores 5 y 6 en su salida estándar.

EJEMPLOS DE USO Var

pto1, pto2, pto3: integer;

begin

new(pto1);

new(pto2);

{ pto1  pto2 } pto1:= 1;

pto2:= 2;

{pto1 = 1  pto2 = 2  pto1  pto2 } pto3 := pto1;

{pto1=1pto2=2pto3=1pto1pto2pto1pto3  pto2 pto3}

pto1:= pto2;

{pto1=2pto2=2pto3=2pto1pto2pto1pto3  pto2 pto3}

dispose(pto3);

{pto2=2pto1=indefinido pto3=indefinido}

end.

(15)

I

MPLEMENTACIÓN DE UNA PILA

. Representación vectorial

P1 P2 ... Pn



tope

 Apilar un elemento:

P1 P2 ... Pn V



tope

 Desapilar un elemento:

P1 P2 ... Pn-1 Pn



tope

tipo pila = tupla

datos: vector [1..MaxP] de elemento;

tope: Natural;

ftupla

 Si p es de tipo pila, la información está en p.datos[1..p.tope]

{}

procedimiento creap (sal p:pila) p.tope := 0

fprocedimiento {p= }

{p = P1 P2.. Pn  v = V }

procedimiento apilar (ent/sal p: Pila; v: elemento) si p.tope<MaxP entonces

p.tope := p.tope + 1; p.datos[p.tope]:=v sino error(‘La pila está llena’)

fprocedimiento {p = P1 P2.. Pn V}

{p = P1 P2.. Pnp <> }

procedimiento desapilar (ent/sal p: Pila) p.tope := p.tope - 1

fprocedimiento {p = P1 P2.. Pn-1}

{p = P1 P2.. Pn p <> }

función topep (p: Pila) devuelve elemento devuelve p.datos[p.tope]

ffunción

{ p = P1 P2.. Pn topep(p) = Pn} { p = P1 P2.. Pn }

función vaciap? (p:Pila) devuelve Lógico devuelve (p.tope = 0)

ffunción

{ p = P1 P2.. Pn vaciap?(p) = (p = )}

 El coste temporal de las operaciones es óptimo, (1)

 El coste espacial es (MaxP) independientemente del número de elementos que la formen.

(16)

Representación enlazada usando variable dinámica

tipo pila = Nodo Nodo = tupla

dato: elemento;

sig: pila;

ftupla;

{}

procedimiento creap (sal p: pila) p := nil

fprocedimiento {p= }

{p = P1 P2.. Pn  v = V }

procedimiento apilar (ent/sal p: pila;v: elemento) var nuevo: pila fvar;

new(nuevo); nuevo^.dato := v; nuevo^.sig := p; p := nuevo;

fprocedimiento {p = P1 P2.. Pn V}

{p = P1 P2.. Pnp <> }

procedimiento desapilar (ent/sal p:pila) var borra:pila;

borra := p; p := p^.sig; dispose(borra);

fprocedimiento {p = P1 P2.. Pn-1}

{p = P1 P2.. Pn p <> }

función topep (p:pila) devuelve elemento devuelve p^.dato;

ffunción

{ p = P1 P2.. Pn topep(p) = Pn} { p = P1 P2.. Pn }

función vaciap? (p:pila) devuelve Lógico devuelve (p=nil)

ffunción

{ p = P1 P2.. Pn vaciap?(p) = (p = )}

Estudio comparativo de las implementaciones

• La complejidad temporal de todas las operaciones en ambas representaciones es independiente de la talla del problema.

• En cuanto a complejidad espacial, la implementación vectorial presenta el inconveniente de la estimación del tamaño máximo y la reserva de espacio que en muchos casos no se utilizará. La representación enlazada requiere un espacio de memoria adicional para los enlaces.

(17)

I

MPLEMENTACIÓN DE

C

OLAS

Representación vectorial

Los elementos de la cola se distribuyen secuencialmente en un vector; además necesitamos para su representación:

1.- un marcador (“libre”) de la posición donde insertar el siguiente elemento (marcador al primer elemento libre de la cola).

a b c

 

libre MaxC

¿qué pasa si desencolamos?

Si el 1er elemento de una cola fuera siempre el que ocupa la 1ª posición del vector, cada operación de borrado, “desencolar”, supondría el desplazamiento del resto de elementos de la cola una posición hacia la izquierda;

a b c

 

libre MaxC

desencolar(q)

b c

 

libre MaxC

desplazar(q);

b c

 

libre MaxC

¡extremadamente ineficiente!

2.- Necesitamos también un marcador de primer elemento de la cola, que llamaremos “prim”

a b c

  

prim libre MaxC

Como consecuencia de tener sólo los marcadores “libre” y “prim”

para manejar el vector-cola, es posible que cualquiera de estos dos marcadores (o ambos) llegen al final del vector (MaxC)

¡SIN QUE LA COLA ESTÉ TOTALMENTE LLENA!

Para poder reutilizar todas las posiciones del vector sin tener que desplazar elementos, y así llegar a su final con todas sus posiciones ocupadas:

Consideraremos el vector como una estructura circular, sin principio ni fin, donde después del último elemento va el primero

...

....

MaxC

1

2

prim libre

COLA c

COLA_VACIA: prim= libre COLA_LLENA: prim = libre

(18)

3. Aún con la estructura circular propuesta NO hay suficiente información para decidir si una cola está vacía o no, ya que la condición de cola vacía es la misma que la de cola llena. Por tanto, además de la estructura circular con dos marcadores, el vector-cola necesita de un tercer marcador: el indicador de cola vacía o llena.

Hemos implementado este indicador como un contador de elementos de la cola.

Representación vectorial circular

tipo

cola = tupla

datos = vector [1..MaxC] of elemento;

prim, libre: Nat;

long: Nat;

ftupla

Si q es de tipo cola, la información está en q.datos[q.prim..q.libre-1]

...

....

MaxC

1

2

c.prim c.libre

COLA c

COLA_VACIA: (c.prim = c.libre)  (c.long = 0) COLA_LLENA: (c.prim = c.libre)  (c.long MaxC)

{}

procedimiento creaq (e/s q:cola) q.pri:= 1; q.libre:= 1; q.long:=0;

fprocedimiento {q = }

{ q=Q1 Q2 .. Qn e=E }

procedimiento encolar (e/s q:cola; e:elemento) si q.long<MaxC entonces

q.elementos[q.libre]:=e;

q.libre:=siguiente(q.libre);

q.long:=q.long+1 sino error (‘Cola llena’) fprocedimiento

{q=Q1 Q2 .. Qn E}}

{q=Q1 Q2 .. Qn q  }

procedimiento desencolar (e/s q:cola) q.pri:=siguiente(q.pri);

q.long:=q.long-1;

fprocedimiento {q=Q2 .. Qn }

{q=Q1 Q2 .. Qn q  }

función primero (q:cola) devuelve elemento devuelve q.datos[q.pri]

fprocedimiento

{ q=Q1 Q2 .. Qn primero(q) = Q1}

(19)

{ q=Q1 Q2 .. Qn }

función vaciaq? (q:cola) devuelve lógico;

devuelve (q.long=0) ffunción

{ q=Q1 Q2 .. Qn b=(q= }

Función privada de la implementación de la cola

{1 i MaxC}

función siguiente (i:Nat) devuelve Nat devuelve (i mod MaxC) + 1

ffunción

{(1 i MaxC siguiente(i) = i+1)



i = MaxC siguiente(i) = 1)}

 El coste temporal de las operaciones es (1) .

Representación enlazada usando variable dinámica.

Ejercicio.

Implementar el TAD COLA utilizando una representación enlazada con variables dinámicas.

tipo

enlace= nodo;

cola= tupla

prim,ult:enlace;

long:nat;

ftupla nodo= tupla

dato:elemento;

sig:enlace;

ftupla

Nota: En este caso el marcador señala al último elemento de la cola y NO al primer elemento libre (como en la implementación con vector circular).

(20)

I

MPLEMENTACIÓN DE LISTAS CON PUNTO DE INTERÉS

Representación secuencial:

Vector en el que los elementos de la lista se almacenan en posiciones contiguas

const MaxL = --;

tipo

posición= Natural;

lista= tupla

datos: vector [1..MaxL] de elemento;

ult, pos: posición;

ftupla

datos L1 L2 L3 ... Ln ...

1 2 3  MaxL

ult

No se utiliza apenas, por ser una representación ineficiente, puesto que:

- la operación de inserción supone el desplazamiento (una posición a la derecha) de todos los elementos posteriores

- la operación de borrado supone el desplazamiento (una posición a la izquierda) de todos los elementos posteriores

Representación enlazada

 Para evitar los desplazamientos de los valores se utiliza una representación enlazada, en la que los elementos no están almacenados en el mismo orden que ocupan en la lista.

 Esta representación enlazada requiere guardar junto con cada elemento la dirección del siguiente elemento de la lista.

 La disposición de los elementos en el vector es irrelevante y depende sólo de la historia de la estructura y de la implementación de los algoritmos

Dos posibles implementaciones: Vectores o Variables dinámicas

(a) Utilizando vectores

Para definir listas enlazadas se utiliza un vector de nodos con la información del nodo más la posición que ocupa dentro del vector su elemento sucesor.

const Máximo= ---;

tipo

posición=Natural ; Nodo= tupla

e:elemento;

pos: posición;

ftupla

MEMORIA= vector [1.. Máximo] de NODO;

Lista=tupla

pri, ult, pos:posición ftupla;

(21)

Convención: el último elemento tendrá el 0 como indicador de su sucesor

Obsérvese cómo en esta estructura se puede representar más de una lista, e incluso otras estructuras.

Ejemplo: lista de enteros = 20,23,25,30,35

1 30 4

2 20 5 pri

3 25 1

4 35 0 ult

5 23 3

...

... ...

Máximo

Ejercicio: Implementar las operaciones del tipo Lista con punto de interés utilizando una representación vectorial enlazada. Para ello, supóngase que se dispone de dos funciones predefinidas,

obten_posnodo(var m:memoria) devuelve posición libera_nodo(p:posición; var m: memoria)

(b) Utilizando variables dinámicas

 Una lista queda representada por una secuencia de nodos enlazados y tres punteros: pri, ult y ant.

 Para mejorar las operaciones de borrado e inserción y facilitar el tratamiento de la lista vacía, el primer nodo de la lista es

‘ficticio’, y tiene posición 0.

 pri, puntero al primer nodo existente,

 ult, puntero al último nodo

 ant, puntero al nodo anterior a la posición distinguida tipo

ptr = Nodo;

Nodo= tupla

dato:elemento;

sig:ptr;

ftupla Lista= tupla

pri, ult, ant: ptr;

ftupla

pri ult

L1 L2 L3

(22)

{}

procedimiento creal (e/s l:lista) var aux:ptr fvar

new(aux); aux.sig:=NIL;

l.pri:= aux; l.ult:= aux; l.ant:= aux fprocedimiento

{l =  pos=1}

{l =L1L2..Ln pos=i  1posn+1 e=E }

procedimiento inserta (e/s l:lista; e:elemento) var q:ptr fvar

new(q); q.dato:=e; q.sig:=l.ant.sig;

l.ant.sig:=q;

si l.ant=l.ult entonces l.ult:=q fsi l.ant:=q

fprocedimiento

{l=L’1L’2...Ln+1(L’1=L1...L’i-1=Li-1)L’i=E(L’i+1=Li...L’n+1=Ln)pos=i+1}

{l =L1L2...Ln pos=i  1posn } procedimiento suprime (e/s l:lista) var q:ptr fvar

si l.ant.sig = l.ult entonces l.ult:= l.ant fsi;

q:=l.ant.sig;

l.ant.sig:=l.ant.sig.sig;

dispose(q) fprocedimiento

{l=L’1 L’2...Ln-1(L’1=L1...L’i-1= Li-1)(L’i=Li+1...L’n-1= Ln) pos=i } {l = L1L2...Ln  1posn }

función recupera (l:lista) devuelve elemento devuelve (l.ant.sig.dato)

ffunción

{ recupera(l)=Lpos l = L1L2...Ln }

{l=L1 L2...Ln  0n}

procedimiento principio (e/s l:lista);

l.ant:= l.pri;

f procedimiento {l=L1L2...Ln pos=1}

{l=L1L2...Ln  0n}

procedimiento fin (e/s l:lista);

l.ant:= l.ult;

ffprocedimiento {l=L1L2...Ln pos=n+1}

{l=L1L2...Ln 1posn  pos=i}

procedimiento siguiente (e/s l:lista);

l.ant:=l.ant.sig;

f procedimiento { l=L1L2...Ln pos=i+1}

{l=L1L2...Ln  0n}

función esvacial (l:Lista) devuelve Lógico devuelve (l.pri=l.ult)

ffunción

{ esvacial(l)=(n=0)  l=L1L2...Ln } {l=L1L2...Ln  0n}

función esfinl (l:Lista) devuelve Lógico devuelve (l.ant=l.ult)

ffunción

{ esfin(l)=(pos=n+1)  l=L1L2...Ln }

(23)

Estudio comparativo de las implementaciones de Listas

COMPLEJIDAD TEMPORAL Secuencial Enlazada

creal(l) (1) (1)

inserta(l, x)  (Nºelem) (1)

suprime(l)  (Nºelem) (1)

e:=recupera(l) (1) (1)

b:=esvacial(l) (1) (1)

fin(l) (1) (1)

primero(l) (1) (1)

Siguiente(l) (1) (1)

b:=esfin(l) (1) (1)

COMPLEJIDAD ESPACIAL

(MaxL) (Nºelem)

• La asignación enlazada requiere un espacio de memoria adicional para los enlaces.

• Es fácil insertar o borrar un elemento en cualquier parte de una lista cuando se usan enlaces, pues solo hay que cambiar dos de ellos. En el caso secuencial esta operación representa un gran consumo de tiempo.

• La representación enlazada facilita la unión de dos listas o su separación.

• El esquema de enlaces se presta para obtener estructuras más complejas que las simples listas lineales

Referencias

Documento similar