Skip to Content

Tipos abstractos

Los tipos de datos abstractos son aquellos que:

  • Se definen especificando constructores/operaciones.
  • Pueden haber varias implementaciones para el mismo TAD.
  • El problema muestra qué necesitamos representar y qué operaciones tener.

Para especificarlo debemos:

  • Indicar su nombre
  • Especificar constructores (proc,  fun)(\proc, \; \fun) los cuales permiten crear elementos del tipo especificado.
  • Especificar operaciones (proc,  fun)(\proc, \; \fun) que permiten manipular elementos del tipo especificado.
  • Especificar tipos de cada constructor/operación, explicar su función mediante lenguaje natural
  • Definir precondiciones (restricciones)
  • Especificar operaciones de destrucción que liberan memoria del tipo (en caso necesario)

Su implementación:

  • Definir un nuevo tipo con el nombre de TAD especificado. Se utilizan tipos concretos/definidos.
  • Implementar constructores respetando los tipos.
  • Implementar operaciones respetando los tipos.
  • Implementar operacion de destrucción si es que se ha reservado al contstruir los elementos.
  • Pueden surgir nuevas restricciones que dependen de cómo implementamos el tipo.
  • Pueden necesitarse operaciones auxiliares no especificadas en el tipo.

Listas

Colecciones de elementos de un mismo tipo, de tamaño variable. Todas son vacías o tienen un elemento al comienzo.

Especificación de listas

spec  List  of  whereconstructorsfun  empty()  ret  l:List  of  T{    Crea una lista vacıˊa    }proc  addl(in  e:T,  in/out  l:List  of  T){    Agrega el elemento  e  al comienzo de una lista  l    }destroyproc  destroy(in/out  l:List  of  T){    Libera memoria en caso que sea necesario    }operationsfun  is_empty(l:List  of  T)  ret  b:bool{    Devuelve  True  si  l  es vacıˊa.    }fun  head(l:List  of  T)  ret  e:T{    Devuelve el primer elemento de  l    }{    PRE:  not  is_empty(l)    }proc  tail(in/out  l:List  of  T)  ret  e:T{    Elimina el primer elemento de  l    }{    PRE:  not  is_empty(l)    }proc  addr(in/out  l:List  of  T,  in  e:T)  ret  e:T{    Agrega el elemento  e  al final de la lista  l    }fun  length(l:List  of  T)  ret  n:nat{    Devuelve la cantidad de elementos de  l    }proc  concat(in/out  l:List  of  T,  in  l0:List  of  T)  ret  e:T{    Agrega al final de  l  todos los elementos de  l  en el mismo orden    }fun  index(l:List  of  T,  n:nat)  ret  e:T{    Devuelve el  neˊsimo elemento de la lista    }{    PRE:  length(l)>n    }proc  take(in/out  l:List  of  T,  n:nat){    Deja en  l  los primerosn  elementos, elimina el resto    }proc  drop(in/out  l:List  of  T,  n:nat){    Elimina los primeros  n  de  l    }fun  copy_list(l1:List  of  T)  ret  l2:List  of  T{    Copia los elementos de  l1  en la nueva lista  l2    }\begin{aligned} & \\ &\spec \; List \; \of \; \where \\ &\\ &\cons \\ &\quad \fun \; empty() \; \ret \; l : List \; \of \; T \\ &\quad\{- \;\; \textnormal{Crea una lista vacía} \;\; -\} \\ &\\ &\quad \proc \; addl (\bfin \; e : T, \; \inout \; l : List \; \of \; T) \\ &\quad\{- \;\; \textnormal{Agrega el elemento} \; e \; \textnormal{al comienzo de una lista} \; l \;\; -\} \\ &\\ &\destroy \\ &\quad \proc \; destroy(\inout \; l : List \; \of \; T) \\ &\quad\{- \;\; \textnormal{Libera memoria en caso que sea necesario} \;\; -\} \\ &\\ &\ops \\ &\quad \fun \; is\_empty(l : List \; \of \; T) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; l \;\textnormal{es vacía.} \;\; -\} \\ &\\ &\quad \fun \; head(l : List \; \of \; T) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Devuelve el primer elemento de} \; l \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty(l) \;\; -\} \\ &\\ &\quad \proc \; tail(\inout \; l : List \; \of \; T) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Elimina el primer elemento de} \; l \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty(l) \;\; -\} \\ &\\ &\quad \proc \; addr(\inout \; l : List \; \of \; T, \; \bfin \; e : T) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Agrega el elemento} \; e \; \textnormal{al final de la lista} \; l \;\; -\} \\ &\\ &\quad \fun \; length(l : List \; \of \; T) \; \ret \; n : \nat \\ &\quad\{- \;\; \textnormal{Devuelve la cantidad de elementos de} \; l \;\; -\} \\ &\\ &\quad \proc \; concat(\inout \; l : List \; \of \; T, \; \bfin \; l0 : List \; \of \; T) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Agrega al final de} \; l \; \textnormal{todos los elementos de} \; l- \; \textnormal{en el mismo orden} \;\; -\} \\ &\\ &\quad \fun \; index(l : List \; \of \; T, \; n : \nat) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Devuelve el} \; n\textnormal{ésimo elemento de la lista} \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; length(l) > n \;\; -\} \\ &\\ &\quad \proc \; take(\inout \; l : List \; \of \; T, \; n : \nat) \\ &\quad\{- \;\; \textnormal{Deja en} \; l \; \textnormal{los primeros} n \; \textnormal{elementos, elimina el resto} \;\; -\} \\ &\\ &\quad \proc \; drop(\inout \; l : List \; \of \; T, \; n : \nat) \\ &\quad\{- \;\; \textnormal{Elimina los primeros} \; n \; \textnormal{de} \; l \;\; -\} \\ &\\ &\quad \fun \; copy\_list(l1 : List \; \of \; T) \; \ret \; l2 : List \; \of \; T \\ &\quad\{- \;\; \textnormal{Copia los elementos de} \; l1 \; \textnormal{en la nueva lista} \; l2 \;\; -\} \\ &\\ \end{aligned}

Para usar el tipo de las listas basta con especificarlas. No es necessario conocer la implementación para usar el TAD.

Sus constructores empty,  addlempty, \; addl permiten crear listas vacías o agregar un elemento nuevo, respectivamente.
Las operaciones permiten manipular listas de acuerdo a la funcionalidad del TAD.

Ejemplo de uso:

fun  promedio(l:List  of  real)  ret  r:realvar  largo:natvar  elem:realvar  laux:List  of  reallaux:=copy(l)r:=0.0largo:=length(l)do  (not  is_empty(laux))elem:=head(laux)r:=r+elemtail(laux)oddestroy(laux)r:=r  /  largoend  proc\begin{aligned} &\fun \; promedio(l : List \; \of \; real) \; \ret \; r : \real \\ &\quad \var \; largo : \nat \\ &\quad \var \; elem : \real \\ &\quad \var \; laux : List \; \of \; \real \\ &\\ &\quad laux := copy(l) \\ &\quad r:=0.0 \\ &\quad largo := length(l) \\ &\quad \do \; (\textnormal{not} \; is\_empty(laux)) \\ &\quad \quad elem := head(laux) \\ &\quad \quad r := r + elem \\ &\quad \quad tail(laux) \\ &\quad \od \\ &\quad destroy(laux) \\ &\quad r:= r \; / \; largo \\ &\bfend \; \proc \end{aligned}

Implementación de listas

La implementación del TAD lista utiliza punteros, dicha implementación es una lista enlazada.
Cada elemento de la lista estará alojado en un nodo conteniendo además un puntero hacia el siguiente.
Una lista será un puntero a un nodo (se sigue un patrón recursivo)

implement  List  of  T  wheretype  Node  of  T=tupleelem:Tnext:pointer  to  (Node  of  T)  end  tupletype  List  of  T=pointer  to  (Node  of  T)\begin{aligned} &\impt \; List \; \of \; T \; \where \\ &\\ &\type \; Node \; \of \; T = \tuple \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad elem : T \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad next : \pointer \; \too \; (Node \; \of \; T) \\ &\quad\quad\quad\quad\quad\quad\quad\quad \; \bfend \; \tuple \\ &\\ &\type \; List \; \of \; T = \pointer \; \too \; (Node \; \of \; T) \\ \end{aligned}

El constructor emptyempty (lista vacía) se implementa fácilmente con el puntero null\null

fun  empty()  ret  l:List  of  Tl:=nullend  fun\begin{aligned} &\fun \; empty() \; \ret \; l : List \; \of \; T \\ &\quad l := \null \\ &\bfend \; \fun \end{aligned}

Lo que falta es la implementación del constructor addladdl (para agregar un elemento nuevo):

Para agregar un elemento a una lista ll, definimos un puntero pp y reservamos memoria al mismo.
El puntero pp apunta a un nodo (el cual contiene un elemento y un puntero).
El campo elemelem que apunta el puntero pp es el elemento ee que queremos agregar a ll


lista

Ahora definimos el siguiente elemento de la lista, nextnext. El cual, es el que ya está apuntado por la lista ll


lista

Entonces ll no debe apuntar a e1e_1 sino a ee (nuevo primer elemento)


lista

En el código tenemos:

proc  addl(in  e:T,  in/out  l:List  of  T)var  p:pointer  to  (Node  of  T)alloc(p)pelem:=epnext:=ll:=pend  procfun  is_empty(l:List  of  T)  ret  b:boolb:=(l=null)end  fun{    PRE:  not  is_empty(l)    }fun  head(l:List  of  T)  ret  e:Te:=(lelem)end  fun{    PRE:  not  is_empty(l)    }proc  tail(in/out  l:List  of  T)var  p:pointer  to  (Node  of  T)p:=ll:=lnextfree(p)end  procproc  addr(in/out  l:List  of  T,  in  e:T)var  p,q:pointer  to  (Node  of  T)alloc(p)qelem:=eqnext:=nullif  ¬is_empty(l)  thenp:=lwhile  (pnextchar"338=null)  dop:=pnextodpnext:=qelsel:=qfiend  procfun  length(l:List  of  T)  ret  n:natvar  p:pointer  to  (Node  of  T)n:=0p:=lwhile  (pchar"338=null)  don:=n+1p:=pnextodend  fun\begin{aligned} &\proc \; addl (\bfin \; e : T, \; \inout \; l : List \; \of \; T) \\ &\quad \var \; p : \pointer \; \too \; (Node \; \of \; T) \\ &\quad alloc(p) \\ &\quad p \to elem := e \\ &\quad p \to next := l \\ &\quad l := p \\ &\bfend \; \proc \\ &\\ &\fun \; is\_empty(l : List \; \of \; T) \; \ret \; b : \bool \\ &\quad b := (l = \null) \\ &\bfend \; \fun \\ &\\ &\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty(l) \;\; -\} \\ &\fun \; head(l : List \; \of \; T) \; \ret \; e : T \\ &\quad e := (l \to elem) \\ &\bfend \; \fun \\ &\\ &\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty(l) \;\; -\} \\ &\proc \; tail(\inout \; l : List \; \of \; T) \\ &\quad \var \; p : \pointer \; \too \; (Node \; \of \; T) \\ &\quad p := l \\ &\quad l := l \to next \\ &\quad free(p) \\ &\bfend \; \proc \\ &\\ &\proc \; addr(\inout \; l : List \; \of \; T, \; \bfin \; e : T) \\ &\quad var \; p,q : \pointer \; \too \; (Node \; \of \; T) \\ &\quad alloc(p) \\ &\quad q \to elem := e \\ &\quad q \to next := \null \\ &\quad \if \; \neg is\_empty(l) \; \then \\ &\quad\quad p := l \\ &\quad\quad \while \; (p \to next \not=\null) \; \do \\ &\quad\quad\quad p := p \to next \\ &\quad\quad \od \\ &\quad\quad p \to next := q \\ &\quad \else \\ &\quad\quad l := q \\ &\quad \fi \\ &\bfend \; \proc \\ &\\ &\fun \; length(l : List \; \of \; T) \; \ret \; n : \nat \\ &\quad \var \; p : \pointer \; \too \; (Node \; \of \; T) \\ &\quad n := 0 \\ &\quad p := l \\ &\quad \while \; (p \not=\null) \; \do \\ &\quad \quad n := n + 1 \\ &\quad \quad p := p \to next \\ &\quad \od \\ &\bfend \; \fun \\ \end{aligned}

Contador

El TAD contador se define por sus operaciones de inicializar un valor, incrementarlo, comprobar si su valor es el inicial y decrementarlo en caso de no serlo.

  • La inicialización e incrementación son los constructores (generan todos los valores posibles del contador).
  • La comprobación solo examina el contador,
  • La decrementación no genera más valores que los obtenibles por inicializar/incrementar (no se decrementa si el valor es nulo)

Especificación de Contador

spec  Counter  whereconstructorsproc  init(out  c:Counter){    Crea un contador inicial    }proc  inc(in/out  c:Counter){    Incrementa el contador  c    }destroyproc  destroy(in/out  c:Counter){    Libera memoria en caso necesario    }operationsfun  is_init(c:Counter)  ret  b:bool{    Devuelve  True  si  c  es el valor inicial    }proc  dec(in/out  c:Counter){    Decrementa el contador  c    }{    PRE:  not  is_init(c)    }\begin{aligned} &\spec \; Counter \; \where \\ &\\ &\cons \\ &\quad \proc \; init(\out \; c: Counter) \\ &\quad\{- \;\; \textnormal{Crea un contador inicial} \;\; -\} \\ &\\ &\quad \proc \; inc(\inout \; c : Counter) \\ &\quad\{- \;\; \textnormal{Incrementa el contador}\; c \;\; -\} \\ &\\ &\destroy \\ &\quad \proc \; destroy(\inout \; c : Counter) \\ &\quad\{- \;\; \textnormal{Libera memoria en caso necesario} \;\; -\} \\ &\\ &\ops \\ &\quad \fun \; is\_init(c : Counter) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; c \;\textnormal{es el valor inicial} \;\; -\} \\ &\\ &\quad \proc \; dec(\inout \; c : Counter) \\ &\quad\{- \;\; \textnormal{Decrementa el contador} \; c \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_init(c) \;\; -\} \\ \end{aligned}

Implementación de Contador

La implementación de un contador es sencilla, se puede hacer con un natural.

implement  Counter  wheretype  Counter=natproc  init(out  c:Counter)c:=0end  procproc  inc(in/out  c:Counter)c:=c+1end  procfun  is_init(c:Counter)  ret  b:boolb:=(c=0)end  fun{    PRE:  not  is_init(c)    }proc  dec(in/out  c:Counter)c:=c1end  procproc  destroy(in/out  c:Counter)skipend  proc\begin{aligned} &\impt \; Counter \; \where \\ &\\ &\type \; Counter = \nat \\ &\\ &\proc \; init(\out \; c : Counter) \\ &\quad c := 0 \\ &\bfend \; \proc \\ &\\ &\proc \; inc(\inout \; c : Counter) \\ &\quad c := c + 1 \\ &\bfend \; \proc \\ &\\ &\fun \; is\_init(c : Counter) \; \ret \; b : \bool \\ &\quad b := (c = 0) \\ &\bfend \; \fun \\ &\\ &\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_init(c) \;\; -\} \\ &\proc \; dec(\inout \; c : Counter) \\ &\quad c := c - 1 \\ &\bfend \; \proc \\ &\\ &\proc \; destroy(\inout \; c : Counter) \\ &\quad \skip \\ &\bfend \; \proc \\ \end{aligned}

Todas las operaciones son O(1)\bigO(1) (constantes).

Pila

Una pila es una colección de datos de mismo tipo inicializada en vacía. Se pueden apilar y desapilar elementos, comprobar si hay primer elemento o examinarlo.
La pila sigue el principio LIFO (Last In, First Out)

Especificación de pila

spec  Stack  of  T  whereconstructorsfun  empty_stack()  ret  s:Stack  of  T{    Crea una pila vacıˊa    }proc  push(in  e:T,  in/out  s:Stack  of  T){    Agrega el elemento  e  al tope de la pila  s    }destroyproc  destroy(in/out  s:Stack  of  T){    Libera memoria en caso necesario    }operationsfun  is_empty_stack(s:Stack  of  T)  ret  b:bool{    Devuelve  True  si  s  estaˊ vacıˊa    }fun  top(s:Stack  of  T)  ret  e:T{    Devuelve el elemento en el tope de  s    }{    PRE:  not  is_empty_stack(s)    }proc  pop(in/out  s:Stack  of  T){    Elimina el elemento en el tope de  s    }{    PRE:  not  is_empty_stack(s)    }fun  is_full(s:Stack)  ret  b:bool{    Devuelve  True  si  s  estaˊ llena    }\begin{aligned} &\spec \; Stack \; \of \; T \; \where \\ &\\ &\cons \\ &\quad \fun \; empty\_stack() \; \ret \; s: Stack \; \of \; T \\ &\quad\{- \;\; \textnormal{Crea una pila vacía} \;\; -\} \\ &\\ &\quad \proc \; push(\bfin \; e : T, \; \inout \; s : Stack \; \of \; T) \\ &\quad\{- \;\; \textnormal{Agrega el elemento} \; e \; \textnormal{al tope de la pila} \; s \;\; -\} \\ &\\ &\destroy \\ &\quad \proc \; destroy(\inout \; s : Stack \; \of \; T) \\ &\quad\{- \;\; \textnormal{Libera memoria en caso necesario} \;\; -\} \\ &\\ &\ops \\ &\quad \fun \; is\_empty\_stack(s : Stack \; \of \; T) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; s \;\textnormal{está vacía} \;\; -\} \\ &\\ &\quad \fun \; top(s : Stack \; \of \; T) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Devuelve el elemento en el tope de} \; s \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty\_stack(s) \;\; -\} \\ &\\ &\quad \proc \; pop(\inout \; s : Stack \; \of \; T) \\ &\quad\{- \;\; \textnormal{Elimina el elemento en el tope de} \; s \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty\_stack(s) \;\; -\} \\ &\\ &\quad \fun \; is\_full(s : Stack) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \; \textnormal{si} \; s \; \textnormal{está llena} \;\; -\} \\ \end{aligned}

Implementación de pila

Se puede utilizar un arreglo o una lista enlazada para implementar una pila, en este caso se usará un arreglo:

implement  Stack  of  T  wheretype  Stack  of  T=tupleelems:array[1..N]  of  Tsize:nat    end  tupleproc  empty(out  s:Stack)s.size:=0end  procproc  push(in  e:T,  in/out  s:Stack)s.size:=s.size+1s.elems[s.size]:=eend  procfun  top(s:Stack)  ret  e:Te:=s.elems[s.size]end  funproc  pop(in/out  s:Stack)s.size:=s.size1end  procfun  is_empty_stack(s:Stack)  ret  b:boolb:=(s.size=0)end  funfun  is_full(s:Stack)  ret  b:boolb:=(s.size=N)end  funproc  destroy(in/out  s:Stack)while  (¬is_empty_stack(s))  dopop(s)odend  proc\begin{aligned} &\impt \; Stack \; \of \; T \; \where \\ &\\ &\type \; Stack \; \of \; T = \tuple \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad elems: \array[1..N] \; \of \; T \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad size: \nat \\ &\quad\quad\quad\quad\quad\quad\quad\quad \;\; \bfend \; \tuple \\ &\\ &\proc \; empty(\out \; s: Stack) \\ &\quad s.size := 0 \\ &\bfend \; \proc \\ &\\ &\proc \; push(\bfin \; e : T, \; \inout \; s : Stack) \\ &\quad s.size := s.size + 1 \\ &\quad s.elems[s.size] := e \\ &\bfend \; \proc \\ &\\ &\fun \; top(s: Stack) \; \ret \; e : T \\ &\quad e := s.elems[s.size] \\ &\bfend \; \fun \\ &\\ &\proc \; pop(\inout \; s : Stack) \\ &\quad s.size := s.size - 1 \\ &\bfend \; \proc \\ &\\ &\fun \; is\_empty\_stack(s : Stack) \; \ret \; b : \bool \\ &\quad b := (s.size = 0) \\ &\bfend \; \fun \\ &\\ &\fun \; is\_full(s : Stack) \; \ret \; b : \bool \\ &\quad b := (s.size = N) \\ &\bfend \; \fun \\ &\\ &\proc \; destroy(\inout \; s : Stack) \\ &\quad \while \; (\neg is\_empty\_stack(s)) \; \do \\ &\quad\quad pop(s) \\ &\quad \od \\ &\bfend \; \proc \\ \end{aligned}

Cola

Una cola es una colección de datos de mismo tipo inicializada en vacía. Se puede encolar y decolar elementos, comprobar si es vacía, o examinar el primer elemento.
La cola sigue el principio FIFO (First In, First Out)

Especificación de cola

spec  Queue  of  T  whereconstructorsfun  empty_queue()  ret  q:Queue  of  T{    Crea una cola vacıˊa    }proc  enqueue(in  q:Queue  of  T,  in/out  e:T){    Agrega el elemento  e  al final de la cola  q    }destroyproc  destroy(in/out  q:Queue  of  T){    Libera memoria en caso necesario    }operationsfun  is_empty_queue(q:Queue  of  T)  ret  b:bool{    Devuelve  True  si  q  estaˊ vacıˊa    }fun  first(q:Queue  of  T)  ret  e:T{    Devuelve el elemento al comienzo de  q    }{    PRE:  not  is_empty_queue(q)    }proc  dequeue(in/out  q:Queue  of  T){    Elimina el elemento al comienzo de  q    }{    PRE:  not  is_empty_queue(q)    }\begin{aligned} &\spec \; Queue \; \of \; T \; \where \\ &\\ &\cons \\ &\quad \fun \; empty\_queue() \; \ret \; q: Queue \; \of \; T \\ &\quad\{- \;\; \textnormal{Crea una cola vacía} \;\; -\} \\ &\\ &\quad \proc \; enqueue(\bfin \; q : Queue \; \of \; T, \; \inout \; e : T) \\ &\quad\{- \;\; \textnormal{Agrega el elemento} \; e \; \textnormal{al final de la cola} \; q \;\; -\} \\ &\\ &\destroy \\ &\quad \proc \; destroy(\inout \; q : Queue \; \of \; T) \\ &\quad\{- \;\; \textnormal{Libera memoria en caso necesario} \;\; -\} \\ &\\ &\ops \\ &\quad \fun \; is\_empty\_queue(q : Queue \; \of \; T) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; q \;\textnormal{está vacía} \;\; -\} \\ &\\ &\quad \fun \; first(q : Queue \; \of \; T) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Devuelve el elemento al comienzo de} \; q \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty\_queue(q) \;\; -\} \\ &\\ &\quad \proc \; deq ueue(\inout \; q : Queue \; \of \; T) \\ &\quad\{- \;\; \textnormal{Elimina el elemento al comienzo de} \; q \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty\_queue(q) \;\; -\} \\ \end{aligned}

Implementación de cola

Se puede utilizar un arreglo o una lista enlazada para implementar una pila, en este caso se usará un arreglo:

implement  Queue  of  T  wheretype  Queue  of  T=tupleelems:array[0..N1]  of  Tfst:natsize:nat      end  tupleproc  empty(out  q:Queue)q.fst:=0q.size:=0end  procproc  enqueue(in/out  q:Queue,  in  e:elem)q.elems[(q.fst+q.size)   mod   N]:=eq.size:=q.size+1end  procfun  first(q:Queue)  ret  e:Te:=q.elems[q.fst]end  funproc  dequeue(in/out  q:Queue)q.size:=q.size1q.fst:=(q.fst+1)   mod   Nend  procfun  is_empty_queue(q:Queue)  ret  b:boolb:=(q.size=0)end  funfun  is_full(q:Queue)  ret  b:boolb:=(q.size=N)end  funproc  destroy(in/out  q:Queue)while  (¬is_empty_queue(q))  dodequeue(q)odend  proc\begin{aligned} &\impt \; Queue \; \of \; T \; \where \\ &\\ &\type \; Queue \; \of \; T = \tuple \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad elems: \array[0..N-1] \; \of \; T \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad fst: \nat \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad size: \nat \\ &\quad\quad\quad\quad\quad\quad\quad\quad \;\;\; \bfend \; \tuple \\ &\\ &\proc \; empty(\out \; q: Queue) \\ &\quad q.fst := 0 \\ &\quad q.size := 0 \\ &\bfend \; \proc \\ &\\ &\proc \; enqueue(\inout \; q : Queue, \; \bfin \;e: elem) \\ &\quad q.elems[(q.fst + q.size) \; \textnormal{ mod } \; N] := e \\ &\quad q.size := q.size + 1 \\ &\bfend \; \proc \\ &\\ &\fun \; first(q : Queue) \; \ret \; e : T \\ &\quad e := q.elems[q.fst] \\ &\bfend \; \fun \\ &\\ &\proc \; dequeue(\inout \; q : Queue) \\ &\quad q.size := q.size - 1 \\ &\quad q.fst := (q.fst + 1) \; \textnormal{ mod } \; N \\ &\bfend \; \proc \\ &\\ &\fun \; is\_empty\_queue(q : Queue) \; \ret \; b : \bool \\ &\quad b := (q.size = 0) \\ &\bfend \; \fun \\ &\\ &\fun \; is\_full(q : Queue) \; \ret \; b : \bool \\ &\quad b := (q.size = N) \\ &\bfend \; \fun \\ &\\ &\proc \; destroy(\inout \; q : Queue) \\ &\quad \while \; (\neg is\_empty\_queue(q)) \; \do \\ &\quad\quad dequeue(q) \\ &\quad \od \\ &\bfend \; \proc \\ \end{aligned}

Ejemplo de buffer de datos:

proc  buffer()var  d:datavar  q:Queue  of  dataempty_queue(q)do  (¬finish())if  there_is_product()  thend:=get_product()enqueue(q,d) else  if  there_is_demand()¬is_empty(q)  thend:=first(q)consume(d)fiodend  proc\begin{aligned} &\proc \; buffer() \\ &\quad \var \; d: data \\ &\quad \var \; q: Queue \; \of \; data \\ &\quad empty\_queue(q) \\ &\quad \do \; (\neg finish()) \\ &\quad\quad \if \; there\_is\_product() \; \then \\ &\quad\quad\quad d:= get\_product() \\ &\quad\quad\quad enqueue(q,d) \\ &\quad\quad\ \else \; \if \; there\_is\_demand() \land \neg is\_empty(q) \; \then \\ &\quad\quad\quad d:= first(q) \\ &\quad\quad\quad consume(d) \\ &\quad\quad \fi \\ &\quad \od \\ &\bfend \; \proc \end{aligned}

Sus funciones auxiliares:

  • finish()finish() devuelve TrueTrue si el servicio productor-consumidor ha finalizado.
  • there_is_product(),  there_is_demand()there\_is\_product(), \; there\_is\_demand() comprueban si hay producción o demanda respectivamente.
  • get_product()get\_product() obtiene un producto desde el productor, y consume(d)consume(d) le envía al consumidor el producto dd.

Conjunto finito de elementos

Como su nombre lo indica, es un conjunto de elementos de un mismo tipo con una cantidad dinámica pero no infinita.

Como constructores tenemos:

  • El conjunto vacío.
  • Agregar un elemento a un conjunto.

Como operaciones tenemos:

  • Chequear si un elemento pertenece al conjunto.
  • Chequear si el conjunto es vacío.
  • Unir un conjunto a otro.
  • Intersecar un conjunto con otro.
  • Obtener la diferencia entre dos conjuntos.

Especificación de conjunto finito de elementos

spec  Set  of  T  whereconstructorsfun  empty_set()  ret  s:Set  of  T{    Crea un conjunto vacıˊo    }proc  add(in/out  s:Set  of  T,  in  e:T){    Agrega el elemento  e  al conjunto  s    }destroyproc  destroy(in/out  s:Set  of  T){    Libera memoria en caso necesario    }operationsfun  is_empty(s:Set  of  T)  ret  b:bool{    Devuelve  True  si  s  es vacıˊo    }fun  member(s:Set  of  T,  e:T)  ret  b:bool{    Devuelve  True  si  e  pertenece al conjunto  s    }proc  union(in/out  s1:Set  of  T,  in  s2:Set  of  T){    Agrega a  s1  los elementos de  s2    }proc  intersection(in/out  s1:Set  of  T,  in  s2:Set  of  T){    Elimina de  s1  los elementos que NO pertenezcan a  s2    }proc  diff(in/out  s1:Set  of  T,  in  s2:Set  of  T){    Elimina de  s1  los elementos que pertenezcan a  s2    }\begin{aligned} &\spec \; Set \; \of \; T \; \where \\ &\\ &\cons \\ &\quad \fun \; empty\_set() \; \ret \; s : Set \; \of \; T \\ &\quad\{- \;\; \textnormal{Crea un conjunto vacío} \;\; -\} \\ &\\ &\quad \proc \; add(\inout \; s : Set \; \of \; T, \; \bfin \; e : T) \\ &\quad\{- \;\; \textnormal{Agrega el elemento} \; e \; \textnormal{al conjunto} \; s \;\; -\} \\ &\\ &\destroy \\ &\quad \proc \; destroy(\inout \; s : Set \; \of \; T) \\ &\quad\{- \;\; \textnormal{Libera memoria en caso necesario} \;\; -\} \\ &\\ &\ops \\ &\quad \fun \; is\_empty(s : Set \; \of \; T) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; s \;\textnormal{es vacío} \;\; -\} \\ &\\ &\quad \fun \; member(s : Set \; \of \; T, \; e : T) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; e \;\textnormal{pertenece al conjunto} \; s \;\; -\} \\ &\\ &\quad \proc \; union(\inout \; s1 : Set \; \of \; T, \; \bfin \; s2 : Set \; \of \; T) \\ &\quad\{- \;\; \textnormal{Agrega a} \; s1 \; \textnormal{los elementos de} \; s2 \;\; -\} \\ &\\ &\quad \proc \; intersection(\inout \; s1 : Set \; \of \; T, \; \bfin \; s2 : Set \; \of \; T) \\ &\quad\{- \;\; \textnormal{Elimina de} \; s1 \; \textnormal{los elementos que NO pertenezcan a} \; s2 \;\; -\} \\ &\\ &\quad \proc \; diff(\inout \; s1 : Set \; \of \; T, \; \bfin \; s2 : Set \; \of \; T) \\ &\quad\{- \;\; \textnormal{Elimina de} \; s1 \; \textnormal{los elementos que pertenezcan a} \; s2 \;\; -\} \\ \end{aligned}

Árboles binarios

Consisten en nodos que almacenan un elemento y dos subestructuras, que pueden ser otro nodo o el vacío.\

  • Uno de sus constructores es el árbol vacío,
  • Su otro constructor es el nodo consistente de un elemento tipo TT y dos árboles.

En el nivel ii de un árbol hay a lo sumo 2i12^{i-1} nodos, (Por ejemplo, nivel 3    43 \implies 4 nodos)

En un árbol “balanceado” la altura es del orden del log2k\log_2 k donde kk es el número de nodos.

Podemos recorrer un árbol indicando caminos desde la raíz y representarlos como secuencias donde los elementos indican si bajamos por izquierda o derecha.

Su especificación es:

type  Direction=enumerateLeftRightend  enumeratetype  Path=List  of  Directionspec  Tree  of  whereconstructorsfun  empty_tree()  ret  t:Tree  of  T{    Crea un aˊrbol vacıˊo    }fun  node(tl:Tree  of  T,  e:T,  tr:Tree  of  T)  ret  t:Tree  of  T{    Crea el nodo con elemento  e  y subaˊrboles  tl,  tr    }destroyproc  destroy(in/out  tr:Tree  of  T){    Libera memoria en caso que sea necesario    }operationsfun  is_empty_tree(t:Tree  of  T)  ret  b:bool{    Devuelve  True  si  t  es vacıˊo    }fun  root(t:Tree  of  T)  ret  e:T{    Devuelve el elemento de la raıˊz de  t    }{    PRE:  not  is_empty_tree(t)    }fun  left(t:Tree  of  T)  ret  tl:Tree  of  T{    Devuelve el subaˊrbol izquierdo de  t    }{    PRE:  not  is_empty_tree(t)    }fun  right(t:Tree  of  T)  ret  tr:Tree  of  T{    Devuelve el subaˊrbol derecho de  t    }{    PRE:  not  is_empty_tree(t)    }fun  height(t:Tree  of  T)  ret  n:nat{    Devuelve la distancia entre la raıˊz de  t  y la hoja maˊs profunda    }fun  is_path(t:Tree  of  T,  p:Path)  ret  b:bool{    Devuelve  True  si  p  es un camino vaˊlido en  t    }fun  subtree_at(t:Tree  of  T,  p:Path)  ret  t0:Tree  of  T{    Devuelve el subaˊrbol de  t  al seguir el camino  p  en  t    }fun  elem_at(t:Tree  of  T,  p:Path)  ret  e:T{    Devuelve el elemento al seguir el camino  p  en  t    }{    PRE:  is_path(t,p)    }\begin{aligned} &\type \; Direction = \enumerate \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad Left \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad Right \\ &\quad\quad\quad\quad\quad\quad\quad\quad \bfend \; \enumerate \\ &\\ &\type \; Path = List \; \of \; Direction \\ &\\ &\spec \; Tree \; \of \; \where \\ &\\ &\cons \\ &\quad \fun \; empty\_tree() \; \ret \; t : Tree \; \of \; T \\ &\quad\{- \;\; \textnormal{Crea un árbol vacío} \;\; -\} \\ &\\ &\quad \fun \; node(tl: Tree \; \of \; T, \; e : T, \; tr: Tree \; \of \; T) \; \ret \; t : Tree \; \of \; T \\ &\quad\{- \;\; \textnormal{Crea el nodo con elemento} \; e \; \textnormal{y subárboles} \; tl, \; tr \;\; -\} \\ &\destroy \\ &\quad \proc \; destroy(\inout \; tr : Tree \; \of \; T) \\ &\quad\{- \;\; \textnormal{Libera memoria en caso que sea necesario} \;\; -\} \\ &\\ &\ops \\ &\quad \fun \; is\_empty\_tree(t : Tree \; \of \; T) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; t \;\textnormal{es vacío} \;\; -\} \\ &\\ &\quad \fun \; root(t : Tree \; \of \; T) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Devuelve el elemento de la raíz de} \; t \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty\_tree(t) \;\; -\} \\ &\\ &\quad \fun \; left(t : Tree \; \of \; T) \; \ret \; tl : Tree \; \of \; T \\ &\quad\{- \;\; \textnormal{Devuelve el subárbol izquierdo de} \; t \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty\_tree(t) \;\; -\} \\ &\\ &\quad \fun \; right(t : Tree \; \of \; T) \; \ret \; tr : Tree \; \of \; T \\ &\quad\{- \;\; \textnormal{Devuelve el subárbol derecho de} \; t \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; \textnormal{not} \; is\_empty\_tree(t) \;\; -\} \\ &\\ &\quad \fun \; height(t : Tree \; \of \; T) \; \ret \; n : \nat \\ &\quad\{- \;\; \textnormal{Devuelve la distancia entre la raíz de} \; t \; \textnormal{y la hoja más profunda} \;\; -\} \\ &\\ &\quad \fun \; is\_path(t : Tree \; \of \; T, \; p : Path) \; \ret \; b : \bool \\ &\quad\{- \;\; \textnormal{Devuelve} \; True \;\textnormal{si} \; p \;\textnormal{es un camino válido en} \; t \;\; -\} \\ &\\ &\quad \fun \; subtree\_at(t : Tree \; \of \; T, \; p : Path) \; \ret \; t0 : Tree \; \of \; T \\ &\quad\{- \;\; \textnormal{Devuelve el subárbol de} \; t \; \textnormal{al seguir el camino} \; p \; \textnormal{en} \; t \;\; -\} \\ &\\ &\quad \fun \; elem\_at(t : Tree \; \of \; T, \; p : Path) \; \ret \; e : T \\ &\quad\{- \;\; \textnormal{Devuelve el elemento al seguir el camino} \; p \; \textnormal{en} \; t \;\; -\} \\ &\quad\{- \;\; \mathbf{PRE:} \; is\_path(t,p) \;\; -\} \\ \end{aligned}

Implementación de árboles binarios

Su implementación es similar a la de listas enlazadas. Se define un tipo NodeNode como tupla con un elemento y dos punteros a sí mismo:

implement  Tree  of  T  wheretype  Node  of  T=tupleleft:pointer  to  (Node  of  T)value:Tright:pointer  to  (Node  of  T)    end  tupletype  Tree  of  T=pointer  to  (Node  of  T)fun  empty_tree()  ret  t:Tree  of  Tt:=nullend  funfun  node(tl:Tree  of  T,  e:T,  tr:Tree  of  T)  ret  t:Tree  of  Talloc(t)tleft:=tltvalue:=etright:=trend  fun\begin{aligned} &\impt \; Tree \; \of \; T \; \where \\ &\\ &\type \; Node \; \of \; T = \tuple \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad left: \pointer \; \too \; (Node \; \of \; T) \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad value: T \\ &\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad right: \pointer \; \too \; (Node \; \of \; T) \\ &\quad\quad\quad\quad\quad\quad\quad\quad \; \; \bfend \; \tuple \\ &\\ &\type \; Tree \; \of \; T = \pointer \; \too \; (Node \; \of \; T) \\ &\\ &\fun \; empty\_tree() \; \ret \; t : Tree \; \of \; T \\ &\quad t := \null \\ &\bfend \; \fun \\ &\\ &\fun \; node(tl: Tree \; \of \; T, \; e : T, \; tr: Tree \; \of \; T) \; \ret \; t : Tree \; \of \; T \\ &\quad alloc(t) \\ &\quad t \to left := tl \\ &\quad t \to value := e \\ &\quad t \to right := tr \\ &\bfend \; \fun \end{aligned}

Árboles binarios de búsqueda

Un arbol binario de búsqueda o ABB es aquel árbol tt donde:

1) Los valores alojados en el subárbol izquierdo de tt deben ser menores que el alojado en la raíz de tt.
2) Los valores alojados en el subárbol derecho de tt deben ser mayores que el alojado en la raíz de tt.
3) Las condiciones anteriores deben cumplirse para todos los subárboles de tt

Si el algoritmo está “balanceado”, es un algoritmo logarítmico.


Si queremos agregar un elemento ee se sigue este procedimiento:

1) Si tt es vacío, se forma el nodo que consta del elemento ee y los dos subárboles vacíos.
2) En caso contrario, se compara el elemento ee con la raíz del árbol tt.

  • Si ee es menor que la raíz, se agrega al subárbol izquierdo.
  • Si ee es mayor o igual a la raíz, se agrega al subárbol derecho.
Última vez actualizado el