Stacks y Colas

Anuncio
ELO320 Estructuras de Datos y Algoritmos
Stacks y Colas
Tomás Arredondo Vidal
Este material está basado en:
 Robert Sedgewick, "Algorithms in C", (third edition), Addison-Wesley, ISBN 0-201-31663-3. 2001
 material del curso ELO320 del Prof. Leopoldo Silva
 material en el sitio http://es.wikipedia.org
6: Stacks
1
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones
6.2 Stacks: implementación
6.3 Colas: definiciones y operaciones
6.4 Colas: implementación
6: Stacks
2
Definiciones y operaciones
 Un stack (o pila) es una lista restringida, en cuanto
a operaciones, ya que sólo permite inserciones y
descartes en un extremo (el tope del stack).
 Tiene gran utilidad al ser usado para implementar
variables automáticas, implementar funciones
recursivas, para evaluar balance de paréntesis
entre otras.
 Operaciones posibles sobre stacks incluyen:



Empujar en el stack o Push
Sacar del stack o Pop
Leer el primer elemento del stack o Read
6: Stacks
3
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones
6.2 Stacks: implementación
6.3 Colas: definiciones y operaciones
6.4 Colas: implementación
6: Stacks
4
Stacks: Implementación
 En general la implementación de las operaciones de
inserción y descarte usando arreglos son costosas, en
comparación con nodos enlazados vía punteros, porque es
necesario desplazar el resto de las componentes después
de una inserción o descarte
 Otro beneficio de usar punteros es que el stack puede
crecer dinámicamente
6: Stacks
5
Stacks: Implementación usando arreglos
#ifndef __STACK_H__
#define __STACK_H__
typedef int Item; // Item almacenado es un int
void StackInit(int);
int StackEmpty(void);
int StackFull(void);
void StackPush(Item);
Item StackPop(void);
void StackDestroy(void);
#endif
/* __STACK_H__ */
6: Stacks
6
Stacks: Implementación usando arreglos (cont)
#include “stack.h”
static Item * stack;
// puntero al inicio de la zona del stack
static int NumItems; // numero items en el stack
static int MAXN;
// Máxima capacidad del stack
void StackInit(int max)
{
stack = malloc(max*sizeof(Item) ); //se solicita arreglo.
if (stack == NULL)
exit(1);
NumItems = 0;
MAXN=max;
}
6: Stacks
7
Stacks: Implementación usando arreglos (cont)
int StackEmpty(void)
{
return(NumItems == 0) ; //Retorna True o 1 si stack vacío
}
int StackFull(void)
{
return(NumItems == MAXN) ; //Ret. True si stack lleno
}
//se puede empujar algo al stack si no está lleno.
void StackPush(Item item)
{
if (!StackFull() )
stack[NumItems ++] = item;
}
6: Stacks
8
Stacks: Implementación usando arreglos (cont)
Item StackPop(void)
{
if( StackEmpty() ) {
printf("error. Extracción de stack vacio\n");
exit(1);
}
else
return ( stack[--NumItems] ) ;
}
void StackDestroy(void)
{
free(stack);
}
6: Stacks
9
Ejemplo: Balance de Parentesis
Es útil poder detectar si es que los paréntesis en un
archivo fuente están o no correctamente balanceados

Ejemplo: a + (b + c) * [(d + e])/f

Seudo-código usando un stack:

Crear el stack.
Mientras no se ha llegado al final del archivo de entrada:
Descartar símbolos que no necesiten ser balanceados.
Si es un paréntesis de apertura: empujar al stack.
Si es un paréntesis de cierre, efectuar un pop y comparar.
Si son de igual tipo continuar
Si son de diferente tipo: avisar el error.
Si se llega al fin de archivo, y el stack no esta vacío: avisar error.
Destruir el stack.
6: Stacks
10
Ejemplo: Expresiones en notacion Polaca Inversa





Las expresiones aritméticas que generalmente
escribimos están en notación “in situ” o fija.
En esta notación los operadores se presentan entre
dos operandos; por ejemplo: 2 + 3 * 4.
Esta notación no explica el orden de precedencia,
esto puede resolver con reglas y con paréntesis:
(2+3)*4
La Reverse Polish Notation inventada por Jan
Lukasiewicz se usa en calculadoras HP resuelve esto .
En notación RPN el operador sigue a los operandos:
423+*
que en “in situ” corresponde a: ( 2 + 3 ) * 4
6: Stacks
11
Ejemplo: Expresiones en notacion Polaca Inversa
(cont)
(3 + 5) * (7 - 2) puede escribirse: 3 5 + 7 2 - *
Leyendo la expresión en RPN se realiza con las
siguientes operaciones:











Push 3 en el stack.
Push 5 en el stack.
El 5 está en el tope, es el último en entrar: (3, 5)
Se aplica la operación + la cual saca los dos números del tope
del stack, los suma y coloca el resultado en el tope: (8)
Push 7 en el stack: (8, 7)
Push 2 en el stack: (8, 7, 2)
Se efectúa la operación – con los dos números en el tope.
Éste contiene ahora (8, 5)
Se efectúa la operación *el stack contiene ahora (40).
6: Stacks
12
Ejemplo: Expresiones en notacion Polaca Inversa
(cont)

Seudo-código
While ( no se haya leído el símbolo fin de archivo EOF)
{
leer un símbolo;
Si es número: empujar el valor del símbolo en el stack
Si es un operador:
{
Efectuar dos pop en el stack;
Ejecutar operación sobre los números;
Empujar el resultado en el stack;
}
}
Retornar contenido del tope del stack como resultado;
6: Stacks
13
Ejemplo: Conversión de in situ a Polaca Inversa
Es útil poder convertir las expresiones infijas a RPN
para poder evaluarlas en un stack.
Para especificar el algoritmo es preciso establecer
las reglas de precedencia de operadores:







La más alta prioridad está asociada a los paréntesis, los
cuales se tratan como símbolos
Prioridad media tienen la operaciones de multiplicación y
división
La más baja la suma y resta.
Se asume solamente la presencia de paréntesis
redondos en expresiones.
Como la notación polaca inversa no requiere de
paréntesis, éstos no se sacarán hacia la salida.
6: Stacks
14
Ej:
While ( no se haya leído el símbolo fin de archivo EOF)
{
leer un símbolo;
Si es número: enviar hacia la salida;
Si es el símbolo ’)’:
no
sacar del stack hacia la salida, hasta encontrar ‘(‘, el cual
debe copiarse hacia la salida.
Si es operador o el símbolo ’(‘:
Si la prioridad del recién leído es menor o igual que la
prioridad
del operado ubicado en el tope del stack:
{ if( tope==‘(‘ ) empujar el operador recién leído;
else { efectuar pop del operador y sacarlo hacia la
hasta que la prioridad del operador recién leído
mayor que la prioridad del operador del tope.
salida
sea
Empujar el recién leído en el tope del stack.
}
}
else empujar recién leído al tope del stack;
} Si se llega a fin de archivo: vaciar el stack, hacia la salida. 6: Stacks
15
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones
6.2 Stacks: implementación
6.3 Colas: definiciones y operaciones
6.4 Colas: implementación
6: Stacks
16
Definiciones y operaciones
 Una cola es una lista con restricciones.
 En ésta las inserciones ocurren en un extremo y los
descartes en el otro (e.g. una cola en el banco)
 Si se conoce el máximo número de componentes
que tendrán que esperar en la cola, se suele
implementar en base a arreglos.
 Requiere dos variables o índices:


cola que es un índice a donde insertar o encolar
cabeza es un índice al elemento a descartar o desencolar
6: Stacks
17
Definiciones y operaciones
 A medida que se consumen o
desencolan componentes, van
quedando espacios disponibles
en las primeras posiciones del
arreglo.

También a medida que se
encolan elementos va
disminuyendo el espacio para
agregar nuevos elementos.

Una mejor utilización del
espacio se logra con un buffer
circular, en el cual la posición
siguiente a la última del
arreglo es la primera del
arreglo.
6: Stacks
18
Buffer circular





Este buffer se puede implementar aplicando
aritmética modular, si el anillo tiene N
posiciones, la operación: cola = (cola + 1) %
N, mantiene el valor de la variable cola
entre 0 y N-1.
Operación similar puede efectuarse para la
variable cabeza cuando deba ser
incrementada en uno.
La variable cola puede variar entre 0 y N-1.
Si cola tiene valor N-1, al ser incrementada
en uno (módulo N), tomará valor cero.
También se agrega una variable N con el
numero de elementos encolados para poder
distinguir entre la cola vacía y llena.
6: Stacks
19
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones
6.2 Stacks: implementación
6.3 Colas: definiciones y operaciones
6.4 Colas: implementación
6: Stacks
20
Colas: Implementación usando arreglos circulares
typedef int Item;
// Item es un entero en este ejemplo
static Item *q;
// Puntero al arreglo de Items
static int N, cabeza, cola, encolados; //Administran el anillo
void QUEUEinit(int maxN) //maxN es el valor N-1 de la Fig
{
q = malloc((maxN+1)*sizeof(Item)); // Espacio para N items
N = maxN+1; cabeza = 0; cola = 0; encolados=0;
}
/* La detección de cola vacía se logra con */
int QUEUEempty()
{
return (encolados == 0);
}
6: Stacks
21
Colas: Implementación (cont)
/* Si la cola no está vacía se puede consumir un elemento */
Item QUEUEget()
{
Item consumido= q[cabeza];
cabeza = (cabeza + 1) % N ;
encolados--;
return (consumido);
}
/* La detección de cola llena se logra con QUEUEfull( )*/
int QUEUEfull()
{
return( encolados == N);
}
6: Stacks
22
Colas: Implementación (cont)
void QUEUEput(Item item) /* Encolar un elemento */
{
q[cola] = item;
cola = (cola +1) % N;
encolados++;
}
void QUEUEdestroy(void) /* Para recuperar el espacio */
{
free ( q );
}

Las funciones cola llena y vacía se podrían implementar
con macros para reducir uso del stack.
#define QUEUEempty() (encolados == 0)
#define QUEUEfull() (encolados == N)
6: Stacks
23
Descargar