elo320 estructuras de datos y algoritmos stacks y colas...
TRANSCRIPT
6: Stacks 1
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 2
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones6.2 Stacks: implementación 6.3 Colas: definiciones y operaciones6.4 Colas: implementación
6: Stacks 3
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 4
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones6.2 Stacks: implementación 6.3 Colas: definiciones y operaciones6.4 Colas: implementación
6: Stacks 5
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 6
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 7
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 8
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 9
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 10
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 11
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:4 2 3 + *
que en “in situ” corresponde a: ( 2 + 3 ) * 4
6: Stacks 12
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 13
Ejemplo: Expresiones en notacion Polaca Inversa (cont)
Seudo-códigoWhile ( 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 14
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 15
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 ’)’:
sacar del stack hacia la salida, hasta encontrar ‘(‘, el cual no 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 salida hasta que la prioridad del operador recién leído sea mayor que la prioridad del operador del tope.
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 16
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones6.2 Stacks: implementación 6.3 Colas: definiciones y operaciones6.4 Colas: implementación
6: Stacks 17
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 18
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 19
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 20
6-Stacks y Colas
6.1 Stacks: definiciones y operaciones6.2 Stacks: implementación 6.3 Colas: definiciones y operaciones6.4 Colas: implementación
6: Stacks 21
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 22
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 23
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)