Está en la página 1de 12

Resumen de los PDF’s - clases de la 3 hasta la 6

Pila: Una pila representa una estructura lineal de datos en la que se puede agregar o quitar
elementos únicamente por uno de los dos extremos.
Utiliza la estructura lifo, el primer elemento que entra, es el último en salir, de la pila solo
podemos acceder al tope, al último elemento que se agregó, no podemos acceder otro lugar
que no sea el tope.

TAD <Pila>
Igualdad Observacional
Si a y b son dos pilas a es igual a b si se cumple que: Las longitudes de a y b son iguales Y
cada elemento en a es igual al correspondiente elemento en b.

Usa
Natural, Bool, Secuencia, None

Parámetro Formal
a
Géneros
Pila <a>

Generadores
vacía () → Pila
{Post: La pila retornada está vacía}

a_partir_de(Secuencia) → Pila
{Post: La pila contiene apilados los elementos de la secuencia recibida}

observadores básicos

tamaño (Pila) → Natural


es_vacía (Pila) → Bool
tope (Pila) → a
{Pre: la pila tiene al menos un elemento}

Otras Operaciones
apilar (Pila, a) → None
{Post: la pila no está vacía}

desapilar(Pila) → a
{Pre: la pila tiene al menos un elemento}
{Post: la pila perdió el tope que tenía antes de desapilar}

Axiomas
vacía (): Crea una pila (sin elementos)
a_partir_de(Secuencia s): crea una pila que contiene apilados los elementos de la secuencia
s siendo el último elemento de la secuencia el que quede en el tope de la pila.
apilar (Pila p, a elem): apila en el tope de p el elemento elem.

desapilar(Pila p): quita el elemento que se encuentra en el tope de p.


tamaño (Pila p): Retorna/devuelve la cantidad de elementos de la pila p.
es_vacía(Pila p): Retorna/devuelve verdadero si la pila p está vacía y falso en caso contrario.
tope (Pila p): retorna/devuelve el elemento que se encuentra en el tope de la pila p.

Exporta
Pila, vacía, a_partir_de, es_vacía, tope, tamaño, apilar, desapilar

Lista doblemente enlazada

Lista doblemente enlazada: Una lista doblemente enlazada es una colección de nodos que
se ordena según su posición, tal que cada uno de ellos es accedido mediante el campo de
enlace del nodo anterior o siguiente. La lista doble es homogénea, dinámica, lineal y tiene
acceso secuencial en ambos sentidos.
TAD
ListaDoble <a>

Igualdad Observacional
Si a y b son dos listas dobles a es igual a b si se cumple que: Las longitudes de a y b son
iguales Y cada elemento en a es igual al correspondiente elemento en b.

Usa
Natural, Bool, Secuencia, None, coordenada<ListaDoble<a>>

Parámetro Formal
a

Géneros
ListaDoble <a>

observadores básicos
tamaño(ListaDoble) → Natural
es_vacia(ListaDoble) → Bool

primero(ListaDoble)→ a
{Pre: la lista doble tiene al menos un elemento}
último(ListaDoble)→ a
{Pre: la lista doble tiene al menos un elemento}

Generadores
vacía () → ListaDoble
{Post: La lista doble retornada está vacía}

a_partir_de(Secuencia)→ ListaDoble
{Post: La lista doble contiene los elementos de la secuencia recibida con el mismo orden que
tienen en la secuencia}

Otras Operaciones

agregar_frente(ListaDoble, a) → None
{Post: la Lista Doble tiene al menos un elemento}

agregar_final(ListaDoble, a) → None
{Post: la Lista Doble tiene al menos un elemento}
Otras Operaciones insertar(ListaDoble, coordenada>, a)→None
{Pre: la coordenada es válida}

{Pos: la Lista Doble no está vacía}


borrar(ListaDoble, coordenada>) → Coordenada>
{Pre: la coordenada es válida}
inicio(ListaDoble)→ coordenada>
fin(ListaDoble)→ coordenada>

Axiomas
vacía (): Crea una lista doble (sin elementos)
a_partir_de(Secuencia s): crea una lista doble que contiene los elementos de la secuencia
recibida con el mismo orden que tienen en la secuencia

insertar(ListaDoble l, coordenada<ListaDoble<a>>
c, a elem): agrega elem en l en la posición que hace referencia la coordenada c

borrar(ListaDoble l, coordenada<ListaDoble<a>>
c): borra de la lista l el elemento que se encuentra en la posición que hace referencia c.
Retorna la coordenada siguiente a c.

tamaño(ListaDoble<a> l): Retorna/devuelve la cantidad de elementos de la lista doble l


es_vacía(ListaDoble<a> l): Retorna/devuelve verdadero si la lista doble l está vacía y falso
en caso contrario
primero(ListaDoble <a> l): retorna/devuelve el primer elemento de la cola l
último(ListaDoble <a> l): retorna/devuelve el último elemento de la lista doble l.
agregar_frente(ListaDoble <a> l, a elem): agrega elem al frente de l

agregar_final(ListaDoble <a> l, a elem): agrega elem al final de l


inicio(ListaDoble <a> l): retorna una coordenada que hace referencia al primer elemento de
l
fin(ListaDoble <a> l): Retorna una coordenada que hace referencia al siguiente del último
elemento de l

Exporta
ListaDoble<a>, vacía, a_partir_de, insertar, borrar, tamaño, es_vacía, primero, último,
agregar_frente, agregar_final, inicio, fin

Coordenada

Como esta lista doble tiene coordenadas, esta última tiene su TAD
Toda coordenada debería soporta dos operaciones básicas: Obtener el elemento al que
hace referencia y avanzar al próximo elemento del contenedor
Como estamos especificando una lista doble su coordenada debería ser bidireccional que
además puede retroceder al elemento anterior

Otra estructura que usa coordenadas es los arboles

Coordenada
TAD
Coordenada <b<a>>

Igualdad Observacional
Si a y b son dos coordenadas a es igual a b si se cumple que: hacen referencia a la misma
posición dentro del mismo contenedor

Usa
Parámetro Formal
b, a

Géneros
Coordenada <b<a>>

observadores básicos
valor (Coordenada <b<a>>) → a
{Pre: La coordenada es válida}

Generadores
a_partir_de(Coordenada <b<a>>) → Coordenada <b<a>>

Otras Operaciones
avanzar (Coordenada <b<a>>) → Coordenada <b<a>>

siguiente (Coordenada <b<a>>) → Coordenada <b<a>>


retroceder (Coordenada <b<a>>) → Coordenada <b<a>>
previo (Coordenada <b<a>>) → Coordenada <b<a>>

Axiomas
a_partir_de(Coordenada <b<a>> c): crea una coordenada a partir de la coordenada c
valor (Coordenada <b<a>> c): retorna el valor al que hace referencia la coordenada c}
siguiente (Coordenada <b<a>> c): retorna una nueva coordenada que hace referencia a la
posición siguiente de c.
previo (Coordenada <b<a>> c): retorna una nueva coordenada que hace referencia a la
posición anterior de c.
avanzar (Coordenada <b<a>> c) avanza a la siguiente a c

retroceder (Coordenada <b<a>> c) retrocede a la anterior a c


Exporta
Coordenada <b<a>>,a_partir_de, siguiente, previo, valor, avanzar, retroceder

Coordenada y lista doblemente enlazada

inicio(ListaDoble<a>) → coordenada < ListaDoble<a>>


fin(ListaDoble<a>) → coordenada< ListaDoble<a>>

Ambos métodos devuelven una referencia del elemento, indicando el inicio y el fin de las
mismas, podemos obtener una de estas coordenadas y hacerlas avanzar para por ejemplo
ingresar un elemento en otra posición.
Fin, devuelve una referencia al siguiente del ultimo
El len en la coordenada no tiene sentido, ya que es una dirección de memoria, no contiene
un conjunto de elementos, no corresponde a una coordenada
Otra cosa a tener en cuenta de las coordenadas es que cuando creamos una, esta no se crea
vacía ya que no tiene sentido crear una coordenada que no apunte a nada, no tendría razón
de ser.
La lista doble contiene un nodo escondido, este no tiene dato y representa el fin de la lista
doblemente enlazada, y lo necesito para implementar la coordenada, para poder
referenciar a alguien que sea el siguiente del ultimo

Implementación de la lista doblemente enlazada


class DoublyLinkedList ():

@dataclass
class _Node:
value: Any
prev: '_Node' = None
next: '_Node' = None

@dataclass
class _Head:
prev: '_Node'= None
next: '_Node'= None

Al crear una lista vacía, esta crea un nodo escondido y se inicia apuntándose a sí misma,
indicando que la lista está vacía
def __init__(self, iterable=None):
self._head = DoublyLinkedList._Head ()

self._head.prev = self._head.next = self._head # Se inicia apuntándose a sí mismo, es el nodo escondido

if iterable is not None:


for value in iterable:
self.append_back(value)

El len lo podemos o hacer un atributo en el nodo, para ir contando o descontando a medida


que vamos agregando o borrando, obteniendo así un orden O (1). También podemos hacer
un método len

def __len__(self):
n=0
nodo = self._head.next
while nodo is not self._head: #corta cuando llega al nodo escondido

n += 1
nodo = nodo.next
return n

Este tiene orden O(n)

def is_empty(self):
return self._head.next is self._head

Este retorna true si el nodo escondido se está auto apuntado, sino false
def front(self):
assert not self.is_empty(), 'lista vacía'

return self._head.next.value ## A menos de que este vacía la lista, devuelve el primer elemento

def back(self):
assert not self.is_empty(), 'lista vacía'

return self._head.prev.value ## A menos de que este vacía la lista, devuelve el último elemento

A partir de ahora los métodos que se van a ver estará relacionados con la clase coordenadas

def insert ( self, coord, value):


current = coord._node
new_node = DoublyLinkedList._Node(value) #creo un nodo nuevo con el valor que me pasan
new_node.prev = current.prev # este engancha el puntero prev al nodo escondido

new_node.next = current #engancha el puntero next al nodo siguiente

new_node.prev.next = new_node #engancha el puntero next del nodo escondido en el nuevo nodo

new_node.next.prev = new_node #encgancha el puntero prev del siguiente nodo al nuevo nodo

En este caso el insert va a hacer 4 enganches, en general hace los enganches necesarios
para que ningún nodo quede en el aire

El insert recibe una coordenada y un valor en el cual voy a insertar, el append_front y


append_back se van a encargar de dar estos datos
Importante saber que el insert inserta antes y después porque es una lista doble, porque si
fuera una lista simple esto no se puede hacer, ya que no tengo acceso al anterior, solo se
puede ingresar antes de... recorriendo la lista, lo ideal es insertar después de...

def append_front(self, value):


self.insert(self.begin(), value)

El aprend front inserta antes del primero, este recibe a una coordenada, en este caso begin
que hace referencia al primer elemento y un valor

def append_back(self, value):

self.insert(self.end(), value)

Este inserta antes del end, quien es el end?el nodo escondido

def begin(self):

return DoublyLinkedList._Coordinate(self._head.next)
Retorna una referencia al primer elemento

def end(self):

return DoublyLinkedList._Coordinate(self._head)
Hace una referencia al nodo escondido, el fin de la lista doble

Implementación de la coordenada

class _Coordinate:
__slots__ = ['_node']

def __init__ (self, coor_or_node): #recibo una coordenada o una referencia


if(isinstance(coor_or_node, DoublyLinkedList._Coordinate)):
self._node = coord_or_node._node #si me mandan una coordenada aceda a su nodo, para
acceder a la referencia

else:
self._node = coor_or_node #sino asigno el parámetro que me pasan

La coordenada se crea a partir de una referencia que a mí me envían, por ejemplo, end,
que pasa una referencia al nodo cabecera

@property
def value(self):

return self._node.value
Da el valor al que se le está haciendo referencia, osea cuando desreferenciamos

@value. setter
def value(self, value):

self._node.value = value
Desreferenciamos un elemento y asignamos un nuevo valor

def __eq__(self, other):


return self._node is other._node

Recursión

Recursión: La Recursividad es una de las Herramientas que permiten expresar la resolución


de Problemas Evolutivos donde es posible que un Módulo de Software se invoque a sí
mismo en la evolución del Problema a la Solución
Si queremos resolver un problema con recursión, tenemos que tener en cuesta si conviene,
por la pila de activación, y también tenemos que tener en cuenta el caso base, que va a ser
necesario para que el programa finalice.
La pila de activación: Cuando un programa comienza su ejecución se crea la pila de
activación, desde aquí cada elemento que se agregue a la pila ocupará un nuevo segmento
de memoria (frame) Cuando un módulo finaliza su ejecución su frame se quita de la pila.

La pila de activación nos va ayudar a hacer ciertas cosas a la vuelta de la recursión, por
ejemplo, sumar los numero que están es esta pila, o hacer los enganches necesarios, pero si
no le encontramos una utilidad, seria de gusto usar la recursión.
En general para un mismo algoritmo la recursión permite una expresión más clara y sintética
lo que simplifica la comprensión y el mantenimiento. A su vez las soluciones recursivas son
normalmente más costosas en tiempo y memoria (menos eficientes).

También podría gustarte