200 UNAN LEON Departamento de Computación Ingeniería en Sistema y Telemática Docente: Ing. Juan Carlos Antón S. Asignatura: Algoritmo y Estructuras de Datos ESTRUCTURAS DINÁMICAS DE DATOS (PILAS)
Pilas Definición Una pila es una colección ordenada de elementos, en una pila se insertan o suprimen los elementos en un extremo al cual se le llama tope ó cima. Esto sugiere la idea de una serie de platos, en la que se van apilando uno encima de otro. En un momento determinado, si se decidiera quitar un plato, el plato que se quitaría sería el último que pusimos en la pila, es decir, el plato que está más arriba; de ahí, el nombre de estructuras tipo LIFO (Last In, First Out) último en entrar, primero en salir. En una pila se pueden agregar nuevos elementos en el tope de la pila o se pueden quitar los elementos que estén en el tope. Elemento T Elemento T- Elemento T-2 Elemento T-3 Elemento T-n Tope de la lista Inicio de la lista El elemento más importante de la pila, es el último elemento insertado (Elemento T), ya que es el primero en ser eliminado. Las operaciones más usadas asociadas a las pilas son: Push (apilar): operación de insertar un elemento en la pila. Pop (desapilar): operación de eliminar un elemento en la pila. Ejemplo: si se tiene una pila p y un elemento i, para insertar un elemento se realiza la operación push (p, i) y para eliminar un elemento se utiliza la operación pop(p) No existe un límite superior para una pila o por lo menos no se acostumbra a que lo tenga, poner un nuevo elemento en la pila solo ocasiona una mayor colección de elementos. Al realizar una operación pop en la pila se debe tener especial consideración al número de elementos que ésta contenga, ya que si la pila está vacía se puede producir un error por acceder a una zona de memoria no valida. Ejemplo: Operaciones sobre una pila, se ha de suponer que la pila está vacía. push (pila, ) push (pila, 5) push (pila, 3) pop (pila) pop (pila) push (pila,8) 5 3 5 8 5
Implementación de una pila utilizando arreglos Las pilas se suelen comparar con los arreglos, un arreglo simula las inserciones y eliminaciones como si se tratara de una pila dinámica. La diferencia de un arreglo con el de una pila es que tiene un tamaño fijo mientras que una pila dinámica crece o decrece conforme a las actividades que realice el usuario. Si hemos de utilizar un arreglo como una pila, siempre se ha de llevar un conteo del número de elementos de la pila para no sobre pasar la dimensión del arreglo. Tanto con el límite superior (tope) como el inferior. La inserción o extracción de un elemento se realiza siempre por la parte superior, su implementación mediante arrays limita el máximo número de elementos que la pila puede contener. A continuación se presentan dos algoritmos para las dos operaciones básicas: Operación Push Inicio Fin Si tope = MaximoElementosPila entonces Imprimir Desbordamiento de la pila Fin-si pila[i] = tope tope = tope + Operación Pop Inicio Fin Si tope = 0 entonces Imprimir Pila Vacia Fin-si Eliminar elemento de la pila tope = tope Ejemplo de una pila implementada con arreglos #include <stdio.h> #include <stdlib.h> #define ELEM_MAX 0 //indica que la pila tendrá un máximo de 0 elementos int top = -; //top o cima indica que la pila está vacía int contador = 0; //lleva el control del total de números ingresados o sacados 2
//Declaracion de operaciones básicas para la manipulación de pilas void Push(int Pila[], int dato); void Pop(int Pila[]); void ConsultarPila(int Pila[], int Total); //Visualizará los valores introducidos dentro de la pila void main() int Pila[ELEM_MAX]; Push(Pila, 0); Push(Pila, 5); Push(Pila, 3); ConsultarPila(Pila, contador); Pop(Pila); Pop(Pila); ConsultarPila(Pila, contador); Push(Pila, ); Push(Pila, 6); ConsultarPila(Pila, contador); Pop(Pila); ConsultarPila(Pila, contador); //Definición de las operaciones básicas void Push(int Pila[], int dato) if (top == ELEM_MAX ) printf("\n\tha ocurrido un desbordamiento en la pila\n\n"); //exit(-); else top += ; Pila[top] = dato; contador++; void Pop(int Pila[]) if (top == -) printf("\n\tla pila está vacía\n\n"); else printf("el numero %d se ha sacado de la pila\n\n",pila[top]); top --; contador--; 3
void ConsultarPila(int Pila[], int Total) int i; printf("\nvalores dentro de la pila:\n\n"); for (i = 0; i < Total; i++) printf("%d\t",pila[i]); printf("\n\n"); Implementación de Pilas con Listas lineales Una vez que se ha visto la implementación de una pila mediante un array, se pasará a ver la implementación de una pila usando una lista lineal. Una pila es una lista lineal en la que todas las inserciones y supresiones (y normalmente todos los accesos), se hacen en un extremo de la lista. Las pilas son muy utilizadas por los compiladores para auxiliarse en el proceso de evaluar expresiones y, para generar código en lenguaje máquina. Las pilas tienen también utilidad cuando se programan juegos de azar. La figura siguiente muestra la representación de una pila usando una lista. Como ejemplo de utilización de una pila, se simulará una calculadora capaz de realizar las operaciones de +, -, * y /. La mayoría de las calculadoras aceptan la notación infija y unas pocas la notación postfija. En esta última para sumar 0 y 20 introduciríamos primero 0, después 20 y por último el +. Cuando se introducen los operandos, se colocan en una pila y cuando se introduce el operador, se sacan dos operandos de la pila. La ventaja de la notación postfija es que expresiones complejas pueden evaluarse fácilmente sin mucho código. La calculadora de este ejemplo utiliza la notación postfija, por lo que hemos desarrollado dos funciones: push y pop. La función push introduce un valor en la pila y la función pop saca un valor de la pila. El programa realiza las siguientes operaciones:. Leer un dato, operando u operador, y lo almacena en la variable op. 2. Analiza op; si se trata de un operando lo mete en la pila utilizando la función push(); y si se trata de un operador saca, utilizando la función pop(), los dos últimos operandos de la pila, realiza la operación indicada por dicho operador y mete el resultado en la pila para encadenarlo con otra posible operación. 3. La función push(), introduce un elemento al inicio de la pila. El método que sigue es simple y sencillo: o Crear un nuevo elemento y referenciarlo por q. 4
o Apuntar con el puntero siguiente de q, a la cima de la pila. o Reasignar la cima de la pila, para que ahora apunte a q. La función pop() sigue estos pasos para obtener un elemento de la pila: o Guarda en una variable auxiliar la cima de la pila. o Verifica el estado de la pila, y si está vacía, regresa 0. o Si la pila no está vacía, entonces o Obtiene en una variable x el dato que se encuentra en el elemento cima. o Reasigna la cima de la pila, para que ahora pase a apuntar adonde apunta su puntero siguiente. Luego de esta operación, la cima de la pila puede ser NULL, lo cual quiere decir que sólo había un elemento, o la cima es diferente de NULL, lo cual indica que había al menos dos elementos. o Libera el espacio de memoria de la cima antigua, referenciado por la variable auxiliar. o Regresa el valor de la variable x. /********** Programa Calculadora. Aplicación de pilas************/ #include <stdio.h> #include <stdlib.h> #define PilaVacia (cima==null) /* Pila Vacia? */ typedef struct datos elemento; /* Tipo elemento */ struct datos /*Estructura de un elemento de la pila*/ float dato; elemento *siguiente; ; /*Funciones*/ void error(void) perror("error: insuficiente espacio en memoria."); exit(); elemento *NuevoElemento() elemento *q=(elemento *)malloc(sizeof(elemento)); return (q); void push(elemento **p, float x); /*añadir un dato a la pila*/ float pop(elemento **p); /*sacar un dato de la pila*/ void main() /*Funcion Principal*/ elemento *cima = NULL; /*Pila Vacia*/ float a,b; char op[8]; system("cls"); printf("calculadora con las operaciones + - * /\n"); printf("los datos seran introducidos de la forma: \n"); 5
printf(">operando \n"); printf(">operando 2\n"); printf("para salir pulse q.\n\n"); do printf("> "); gets(op); switch(*op) case '+': b=pop(&cima); a=pop(&cima); printf("%g\n",a+b); push(&cima,a+b); case '-': b=pop(&cima); a=pop(&cima); printf("%g\n",a-b); push(&cima,a-b); case '*': b=pop(&cima); a=pop(&cima); printf("%g\n",a*b); push(&cima,a*b); case '/': b=pop(&cima); a=pop(&cima); if(b==0) printf("\n\adivision por cero\n"); printf("%g\n",a/b); push(&cima,a/b); default: push(&cima,atof(op)); while(*op!= 'q'); /*Añadir un dato a la pila*/ void push(elemento **p, float x) elemento *q, *cima; cima = *p; /*cima de la pila*/ q=nuevoelemento(); q->dato=x; q->siguiente=cima; cima=q; *p=cima; 6
/*Recuperar un dato de la cima de la pila*/ float pop(elemento **p) elemento *cima; float x; cima = *p; /*cima de la pila*/ if(pilavacia) printf("\nerror: pop de una pila vacia"); return 0; else x = cima->dato; *p = cima->siguiente; free (cima); return (x); La siguiente imagen muestra el programa anterior en ejecución realizando las diferentes operaciones de una calculadora: 7