Вы находитесь на странице: 1из 9

5.

Pilas y Filas

Tanto pilas y filas son un caso especial de un objeto de datos más general, listas secuenciales:

A = {a1 , a2 , . . . , an }, donde n ≥ 0.

5.1. Pilas

Una pila es una lista secuencial donde todas las inserciones y eliminaciones son hechas a un extremo,
llamado el top. Dado una pila S = {a1 , a2 , . . . , an }, diremos que a1 es el elemento más al fondo de
la pila y el elemento ai está al tope del elemento ai−1 .
Cinco son las operaciones comunes cuando se accesan pilas: crear, agregar, eliminar, obtener el tope
y pila vacı́a. Una especificación algebraica de la estructura de pila es la siguiente:

structureSTACK(ITEM)
declare
N EW () → stack
ADD(stack, item) → stack
DELET E(stack) → stack
T OP (stack) → item
IS EM P T Y (stack) → boolean
for all s ∈ ST ACK, i ∈ IT EM let
IS EM P T Y (N EW ()) ::= true
IS EM P T Y (ADD(s, i)) ::= f alse
T OP (N EW ()) ::= error
T OP (ADD(s, i)) ::= i
DELET E(N EW ()) ::= N EW ()
DELET E(ADD(s, i)) ::= s

Un uso de pilas en programación es el procesamiento de llamadas a subrutinas y su retorno. Un sis-


tema operativo ejecuta instrucciones siguiendo la secuencia en el código del programa. Al encontrar
una llamada a rutina, el sistema operativo debe almacenar el lugar de donde se hizo la llamada,
como también, las variables y valores de estas variables hasta antes de la llamada (manejo de scope
de variables). La rutina se ejecuta y una vez que se termina la ejecución, el sistema operativo debe
volver la ejecución al lugar de donde se hizo originalmente la llamada. Gráficamente, veamos el
modelo de pila de un lenguaje de programación (Figura 5).

23
Programa
Puntero a instrucción

Puntero a ambiente

RA2
RA1
memoria (pila)
Registro activación de procedure

identificación variables en bloque


retorno

Link estático Link dinámico

Figura 5: Modelo de Pila de un Lenguaje de Programación

Considere el siguiente algoritmo donde la procedure p(i) usa un pasaje por valor de la variable i:

1 [ integer a
2 Procedure p(integer i) [
3 a := i ]
4 [ integer a
5 a := 1
6 p(a)
7 ]
8 ]
Dado el siguiente algoritmo, la ejecución del algoritmo en el modelo de pila es el siguiente:

24
π π
ep ep
P 3
ip ip a
RA1
1 5 sl dl
pila vacía

π a 1
ep sl dl
P 3
ip a
6 sl dl

i 1
ret 7
π a 1
π a 1
ep sl dl
ep sl dl P 3
P 3 ip a 1
ip a 7 sl dl
3 sl dl

π
π
ep
ep
P 3
RA1
ip
ip a 1 0
8 sl dl

Figura 6: Ejecución de programa

25
Una forma simple de representar pilas es con un arreglo stack(1 : n), donde n es el número máximo
de elementos en la pila y el i-ésimo elemento está localizado en stack(i). Usando esta representación,
la implementación de las funciones quedan definidas de esta forma:
NEW()::= declare stack(1 : n); top := 0
IS EMPTY(stack)::= if top = 0 then true else f alse
TOP(stack)::= if top = 0 then error else stack(top)

Procedure ADD(T item, n, var ST ACK, var top)


if top ≥ n do call ST ACK F U LL
else [
top := top + 1
stack[top] := item ]
end ADD

Procedure DELET E(stack, var top, var item)


if top ≤ 0 do call ST ACK EM P T Y
else [
item := stack[to])
top := top − 1]
end DELET E
Problemas con la representación con arreglos son:

Desperdicio de espacio cuando la pila está vacı́a

Problema de overflow cuando la pila está llena

Como solución parcial se puede usar arreglos con base (partida) variable, lo que significa un movi-
miento de elementos cada vez que existe overflow.
Otro tipo de implementación consiste en usar punteros, es decir, manejar una lista de registros
o nodos unidos por punteros. En este caso, el top de la lista es el primer elementos de la lista y
está designado por el puntero stack. Dada esta representación, un algoritmo para insertar y eliminar
en una pila es, donde p y stack son punteros a elementos de una pila:
Procedure ADD(item, var stack)
p := N EW ()
p → inf o := item
p → next := stack
stack := p
end ADD

Procedure DELET E(var stack)


if IS EM P T Y (stack) do call ST ACK EM P T Y
else [
p := stack;
stack := stack → next;
DELET E(p) ]
end DELET E

26
5.2. Filas

Una fila es una estructura de datos lineal donde se inserta en un extremo de la fila (rear) y se
saca del otro extremo de la fila (f ront). Un conjunto mı́nimo de operaciones con una fila son crear,
agregar, borrar, obtener el elemento al frente y preguntar si la fila está vacı́a. Una especificación
como estructura algebraica es:

structureQUEUE(ITEM)
declare
N EW () → queue
ADD(queue, item) → queue
DELET E(queue) → queue
F RON T (queue) → item
IS EM P T Y (queue) → boolean
for all q, q1 ∈ QU EU E, i ∈ IT EM let
IS EM P T Y (N EW ()) ::= true
IS EM P T Y (ADD(q, i)) ::= f alse
F RON T (N EW ()) ::= error
F RON T (ADD(q, i)) ::= if IS EM P T Y (q) then i else F RON T (q)
DELET E(N EW ()) ::= N EW ()
DELET E(ADD(q, i)) ::= if IS EM P T Y (q) then N EW () else ADD(DELET E(q), i)

Al igual que para pilas, una forma simple de representar filas es con un arreglo queue(1 : n), siendo
n el número máximo de elementos en la fila y siendo rear y f ront las variables que indican el
comienzo y fin de la fila, respectivamente. Usando esta representación, la implementación de las
funciones quedan definidas de esta forma:

NEW()::= declare queue(1 : n); f ront := rear := 0


IS EMPTY(queue)::= if f ront = rear then true else f alse
FRONT(queue)::= if IS EM P T Y (q) then error else queue(f ront + 1)

Procedure ADD(item, n, var queue, var rear)


if rear = n do call QU EU E F U LL
else [
rear := rear + 1
queue[rear] := item ]
end ADD

Procedure DELET E(rear, queue, var f ront, var item)


if f ront = rear do call QU EU E EM P T Y
else [
f ront := f ront + 1
item := queue[f ront]]
end DELET E
La función QUEUE FULL puede ser implementada de tal manera que si hay espacio vacı́o al
comienzo de la fila, los elementos sean movidos de manera de comenzar nuevamente en la posición

27
0. Esto es bastante ineficiente.
Una forma más eficiente de implementar una fila es considerar el arreglo queue(1 : n) circular.
Considere la declaracin de queue(0 : n − 1), cuando rear = n − 1, el próximo elemento que se
ingresa es colocado en la posición queue(0), en caso de que este vacı́o. Las funciones ADD y
DELET E son ahora definidas de la siguiente manera:
Procedure ADD(item, f ront, var queue, var rear)
rear := (rear + 1) mod n
if rear = f ront do call QU EU E F U LL
else
queue[rear] := item
end ADD

Procedure DELET E(rear, queue, var f ront, var item)


if f ront = rear do call QU EU E EM P T Y
else [
f ront := (f ront + 1) mod n
item := queue[f ront]]
end DELET E
En esta implementación se permiten n − 1 elementos y la misma condición se cumple para QUEUE
EMPTY o FULL. En el caso de insertar un elemento en el último espacio disponible, no hay forma
de distinguir entre vacı́o o lleno. En ese caso se puede usar una variable Boolean. ¿ Cómo serı́an los
algoritmos anteriores con esa variable? Una forma es considerar que la variable Boolean mantiene
el tipo de operación que ha sido ejecutada último. En caso de que rear = f ront en un DELET E,
esta condición será vacı́a a menos que la última operación haya sido un ADD, en cuyo caso, la fila
no está vacı́a sino llena.
¿ Cómo se puede expresar el número de elementos de una fila circular en función de n, f ront,
and rear? Considere la representación por arreglo con elementos queue(0 : n − 1). En ese caso, el
número de elementos en la fila (num) está dado por:



 rear − f ront if f ront < rear

rear + (n − 1 − f ront) if rear > f ront
num =

 0 QU EU E EM P T Y

n−1 QU EU E F U LL

La representación con punteros de una fila maneja dos punteros rear and f ront, haciendo este
último equivalente a queue. Los algoritmos ADD y DELET E son:

28
Procedure ADD(item, var queue, var rear)
p := N EW ()
p → inf o := item
p → next := nil
if IS EM P T Y (queue) do queue := rear := p
else [
rear → next := p
rear := p]
end ADD

Procedure DELET E(var queue)


if IS EM P T Y (queue) do call QU EU E EM P T Y
else [
p := queue;
queue := queue → next;
DELET E(p) ]
end DELET E
Ejercicios:
1. Sea F una operación donde su argumento y resultado son una fila y cuyos axiomas son:

F (N EW ()) = N EW ()
F (ADD(i, q)) = if IS EM P T Y (q) then ADD(i, q)
else ADD(F RON T (q), F (DELET E(ADD(i, q)))

¿Qué realiza esta función?


2. Escribir un algoritmo de búsqueda de la salida en un laberinto.

i
j

Figura 7: Ejercicio del laberinto

29
Algoritmo T (n)
Procedure P AT H(laberinto, marca, m, n, mov, stack)
C: laberinto(0 : m + 1, 0 : n + 1)
C: marca(0 : m + 1, 0 : n + 1) es cero en marca(i, j) si es no ha sido
visitada la celda
C: mov(8, 2) es una tabla que describe movimientos posibles
C: stack(mn, 3) es una pila de tres elementos mantiene el camino actual,
es decir, la posición i,j,y el tipo de movimiento
1 stack(1, 1) := 1 1
2 stack(1, 2) := 1 1
3 stack(1, 3) := 2 1
4 top := 1 1
5 while top &= 0 do [ ttop + 1
6 i := stack(top, 1) ttop
7 j := stack(top, 2) ttop
8 m := stack(top, 3) + 1 ttop
9 top := top − 1 ttop
10 while m ≤ 8 do [ ttop ∗ tm + 1
11 g := i + mov(m, 1) ttop ∗ tm
12 h := j + mov(m, 2) ttop ∗ tm
13 if (g = m) and (h = n) do [ ttop ∗ tm
14 for p := 1t o top do top + 1
15 print(stack(p,1),stack(p,2)) top + 1
16 return ] 1
17 if (laberinto(g, h) = 0) and (marca(g, h) = 0 do [ ttop ∗ tm
18 marca[g, h] := 1 (ttop ∗ tm )marca
19 top := top + 1 (ttop ∗ tm )marca
20 stack(top, 1) := i (ttop ∗ tm )marca
21 stack(top, 2) := j (ttop ∗ tm )marca
22 stack(top, 3) := m (ttop ∗ tm )marca
23 m := 0 (ttop ∗ tm )marca
24 i := g (ttop ∗ tm )marca
25 j := h] (ttop ∗ tm )marca
26 m := m + 1] ] (ttop ∗ tm )marca
27 print( ”no hay camino”) 1
end P AT H
En este ejemplo, ttop es el número de veces que se ejecuta el while que comienza en posición 5
(es decir, el número de almacenamientos en la pila), tm es el número de veces que se ejecutan
los movimientos en el while de posición 10 (es decir, el número de veces que consultan diferentes
movimiento a partir de una posición dada, y ()marca es el número de veces que se encuentra una
celda no visitada para el número de movimientos revisados. Una cota superior para este algoritmo
este algoritmo es:

ttop < nm, ttop ∈ O(nm)


tm ≤ 8, tm ∈ O(1)
(ttop ∗ tm )marca < ttop ∗ tm < 8nm, (ttop ∗ tm )marca ∈ O(nm)

30
5.3. Pilas o Filas Múltiples

La idea es poder aprovechar al máximo el espacio disponible para el manejo de múltiples pilas.
Centrándose en la representación basada en arreglos V (1 : m), una solución simple para manejar
dos pilas es usar el V (1) para el elemento más al fondo de una pila y el elemento V (m) para el
correspondiente elemento ( es decir, el de más al fondo) en la segunda pila.
Para n pilas, uno debiera subdividir el espacio, ojalá, en particiones equivalentes. Para cada stack
i se debiera usar B(i) para representar la posición anterior a la del elemento más profundo en la
pila. T (i) , 1 ≤ i ≤ n direcciona al elemento al tope de la pila i. La pila estará vacı́a si B(i) = T (i).
Si la pila i está localizada en ı́ndices de memoria más bajos que i + 1, entonces inicialmente:

m
B(i) = T (i) = ( )(i − 1), 1 ≤ i ≤ n
n

Pila T (i) puede crecer de B(i) +1 a B(i+ 1), donde B(n + 1) = m. Dados B(1 : n) y T (1 : n), los al-
goritmos ADD y DELET E para la pila i e item x con arreglo V (1 : m) para los datos en la pila son :

Procedure ADD(i, item, B, var T, var V )


if T (i) = B(i + 1) do call ST ACK F U LL
else[
T (i) := T (i) + 1
V (T (i)) := item ]
end ADD

Procedure DELET E(i, var T, var x)


if T (i) = B(i) do call ST ACK EM P T Y
else[
x := V (T (i))
T (i) := T (i) − 1]
end DELET E
Una alternativa para a definción de STACK FULL es:

Encontrar el menor j con i ≤ j ≤ n tal que exista espacio libre entre la pila j y j + 1, es decir,
T (j) < B(j + 1). Si existe, mover las pilas i + 1, . . . , j una posición a la derecha creando un
espacio entre pilas i y i + 1.

Si no existe j, entonces mire hacia la izquierda.

Si no existe j en ambas búsquedas, no existe espacio en la pila.

31

Вам также может понравиться