Capítulo 5 Colas
Lección 6 Conceptos básicos de colas Lección 7 Operaciones básicas con colas
Lección 8 Implementación de las Colas por medio de Punteros Lección 9 Implementación del código completo de la cola
Lección 10 Aplicación de una cola en un entorno real planificador de citas Capítulo 6 Listas
Lección 11 Concepto básicos de Listas Lección 12 Listas enlazadas
Lección 13 Más a cerca de listas enlazadas Lección 14 Listas doblemente enlazadas Lección 15 Listas circulares
59 CAPITULO 4: PILAS
Introducción
Las pilas son un tipo de estructura de datos de tipo lineal condicionadas donde las inserciones y eleminaciones se realizan por un mismo extremo. Se tratan los temas relacionados con los conceptos básicos de las pilas, las operaciones que se pueden realizar con las pilas, todo conjugado en programas de aplicación, implementados con apuntadores, al igual que en los anteriores capítulos, cada uno de los programas aquí presentados, están previamente compilados y depurados de tal manera que se mustra la salida en pantalla de cada uno. Lo anterior garantiza al estudiante que puede guiarse en el código fuente para hacerle modificaciones y proponer soluciones a entornos reales.
Lección 1: Conceptos básicos de pilas
La pila se considera un grupo ordenado de elementos porque estos están clasificados de acuerdo al tiempo en que estén residiendo en la pila, el elemento que se elimina de la cabeza es siempre el que lleve menos tiempo en ella. En resumen una pila es una lista en la cual los elementos solo pueden ser insertados y eliminados por un extremo de ella, este extremo es llamado la cima. De esta forma los elementos son eliminados en forma contraria a como fueron insertados.
Definición de una Pila
Una pila llamada stack (en inglés) se puede definir como un tipo especial de lista lineal condicionada, en la que las inserciones y el borrado de nuevos elementos se realizan sólo por un extremo, que se denomina cima o tope top (en inglés). La pila es una estructura con numerosas analogías en la vida real: pila de platos, una pila de monedas, una pila de camisas, una pila de animales, etc. Como se muestra en la figura siguiente.
60 Figura 20 Representación gráfica de una pila de monedas
Fuente: www.utpl.edu.ec/ecc/wiki/images/1/1e/Pilas.JPG
Una pila, a un nivel físico puede representarse tal como se muestra en figura 21, donde representa una pila de moneda o una pila de platos en un restaurante. Cuando se habla de una pila a nivel lógico, es una estructura de datos lineal compuesta de elementos del mismo tipo, en la cual cada uno de ellos solo puede ser insertado y eliminado por su parte final, es decir por el extremo final.
La posición final la llamamos cima o también cabeza de la pila; para darse una mejor representación (abstracción) de ella, su representación gráfica en el papel la se hace en forma vertical, con la cima en la parte de arriba. De esta forma cuando se añade un nuevo elemento solo podrá ser colocado en la parte superior, piense en una pila de platos de cocina o en una pila de libros, los cuales se colocan en orden uno encima de otro. Por lo anterior las pilas también son llamadas listas en las cuales el último elemento en entrar es el primero en salir, en inglés el acrónimo LIFO(Last Input, First Out).
Lección 2: Operaciones realizadas con pilas
Las operaciones básicas que se pueden realizar con la implementación de una lista lineal condicionada tipo pila son: la inserción la eliminación y la visualización de sus elementos. Esta implementación se lleva a cabo haciendo uso de punteros con listas enlazadas al igual que por medio de arreglos unidimensionales.
61 Una Pila es una estructura de datos que almacena elementos de un tipo determinado, tal como se trató en la unidad uno en el tema relacionado con los tipos de datos. Es así como se sigue la política de que el primer elemento que se extrae, es decir se desapila, es el último que se introdujo o se apiló, cumpliendo con el enunciado que dice que el primero en salir, es el ultimo en entrar (LIFO). En la figura 22 se visualiza gráficamente la entrada y salida de elementos de una pila.
Figura 21 Entrada y salida de elementos de una pila
En principio, la pila está vacía y el puntero de la pila o CIMA debe estar en cero. Al insertar un elemento en la pila, se incrementa el puntero en una unidad. Mientras que al sacar un elemento de la pila se decrementa en una unidad el puntero, teniendo aplicabilidad las operaciones que se realizan con punteros tratadas en la Unidad uno. Siendo P el puntero (*P), al manipular una pila se deben realizar algunas comprobaciones.
En una pila vacía no se pueden sacar datos, así que si (P = 0) no es posible eliminar elementos de la pila.
En condiciones Ideales, una pila puede contener un número ilimitado de elementos y no producir nunca desbordamiento. En la práctica, el espacio de almacenamiento disponible es finito dependen de los recursos de memoria de la máquina (los recursos computacionales son finitos). La codificación de una pila requiere un cierto equilibrio, ya que si la longitud máxima de la pila es demasiado grande, se gasta mucha memoria, mientras que un valor pequeño producirá desbordamientos con mucha frecuencia.
62 Para trabajar fácilmente con pilas, es conveniente diseñar funciones o subprogramas para cada una de las operaciones que se realizan con pilas, es decir una función para insertar (push) otra para extraer (pop) o eliminar elementos y la función para visualizar los elementos insertados en la pila. No hay que olvidar comprobar con frecuencia si la pila está vacía; si el puntero es igual a NULL (puntero=NULL).
En la siguiente sección se enumeran los pasos algorítmicamente para la implementación de las operaciones que se realizan con las pilas. Para ello se declaran dos punteros: cima y auxiliar.
Dado que los elementos se incorporan siempre por un extremo se tiene: 1. cima apunta al último elemento de la pila es decir a NULL.
2. Reservar memoria (auxiliar)
3. Se Introduce la información en auxiliar elemento
4. Se hace que auxiliar cima es decir que apunte a donde cima 5. Se cambia cima para que apunte a donde lo hace auxiliar
6. La pila tiene un elemento más. Esta rutina se repite para insertar los elementos requeridos en la pila.
Los elementos se recuperan en orden inverso a como fueron introducidos. 1. Cima apunta al último elemento de la pila.
2. Se hace que auxiliar apunte a donde apuntaba cima 3. Se hace que cima pase a apuntar a donde cima cima 4. Liberar memoria reservada (auxiliar)
5. La pila tiene un elemento menos
El prototipo de nodo típico para construir pilas es a través de las estructuras. struct pila
{
int dato;
struct pila *siguiente; };
Lección 3: Operaciones básicas con pilas paso a paso
Después de haber realizado la lectura detallada y analizado la aplicabilidad que se le puede dar a las pilas, se continúa con la implementación. Tal como se mencionó en capítulos anteriores. La implementación de las estructuras lineales se puede realizar por medio de listas enlazadas con punteros o por medio de arreglos. En este curso profundizaremos con más detalle en la implementación de las listas enlazadas con punteros, la razón se debe a que el tema de los arreglos se trata en
63 detalle en los cursos Algoritmos e introducción a la programación, creando así la necesidad de profundizar en la implementación de las estructuras lineales con el tema de punteros.
Se recomienda seguir las siguientes instrucciones paso a paso y obtendrá la implementación básica de una pila para el manejo de números enteros ingresados en tiempo de ejecución, es decir por teclado, para lo cual se hará uso de un menú de opciones y tres funciones básicas para insertar visualizar y extraer datos de la pila.
Se inicia abriendo un documento nuevo en cualquier editor del cual disponga, inclusive puede trabajar en el bloc de notas que provee su sistema operativo, para este caso usaremos el editor de Turbo C++, ingrese los archivos de cabecera, también llamados librerías, estas son básicas por ser las más usadas.
#include<iostream.h> #include<stdio.h> #include<stdlib.h> #include<conio.h> #include<dos.h>
En este caso se crea una estructura para el manejo de la información que contendrá la pila, aunque solo se manejarán datos de tipo entero, es bueno recordar que una estructura es una colección de una o más variables, agrupadas bajo un solo nombre para facilitar su manejo, a diferencia de las que se encuentran en arreglos, pueden ser de diferentes tipos de variables del C++, incluyendo arreglos y otras estructuras.
Cada variable dentro de una estructura se llama un miembro de la estructura. Después de cerrar la llave de la estructura y del punto y coma, en una línea aparte se declaran las variables apuntadores requeridas para manipular los datos de la pila, estos son del mismo tipo de la estructura inicializados a NULL, son ellos *inicio y *c.
struct pila {
int numero; struct pila *sig; };
struct pila *inicio=NULL; struct pila *c=NULL;
Otra forma de declarar la estructura es poner a continuación de la definición de la estructura una lista de uno o más nombres de variables inicializadas llamadas instancias como se muestra acontinuación.
64 struct pila
{
int numero; struct pila *sig; }*inicio=NULL, *c=NULL;
Para continuar con el programa se escoge la segunda alternativa, que tiene entre sus miembros una variable llamada numero de tipo entero que guardará los números que serán ingresados, al igual que un puntero llamado sig del tipo struct pila, del mismo tipo de la estructura usado para apuntar al siguiente nodo de la pila. Finalmente se declaran como instancias, dos variables apuntador del mismo tipo de la estructura llamadas inicio y c inicializadas a NULL como buena costumbre.
Estas variables son llamadas instancias que son del mismo tipo de la estructura, inicio usada para reservar memoria y c usada para recorrer la pila; una vista de lo hecho hasta ahora es:
Figura 22 Declaración de la estructura pila
Implementación del menú de opciones para manipular la pila
Se continúa con la función principal haciendo uso de un menú de opciones que a mi juicio es la mejor alternativa para interactuar con las estructuras lineales. En este punto es importante compilar el programa para depurar y corregir los posibles errores de sintaxis o de estructura, por lo pronto en las 3 opciones del menú solo desplegará un corto mensaje ya que las funciones de insertar visualizar y extraer aun no han sido declaradas.
65 { textcolor(10); int opc=0; do { clrscr();
cout<<" MANEJO DE UNA ESTRUCTURA TIPO PILA "; cout<<"\n\n\n"); cout<<"1. Insertar\n"); cout<<"2. Extraer\n"); cout<<"3. Visualizar\n"); cout<<"4. Salir\n\n"); cout<<"Digite la opcion: "); cin >>opc; switch (opc) { case 1: cout<<"inserter"; break; case 2: cout<<"extraer"; break; case 3: cout<<"visualizar"; break; case 4: exit(1); } getch(); }while (opc!=4) }
El código de la función main() o también llamada función principal que se incluye en el programa en construcción, no amerita algún tipo de explicación, es conocido y muy usado en el curso Introducción a la Programación.
Guarde el programa con un nombre, puede ser Pila.cpp. Compile y ejecute el programa, si lo digitó cuidadosamente no generará errores de compilación tan poco errores de ejecución, se obtendrá el siguiente resultado después de haber digitado la opción 1.
66 Figura 23 Menú de opciones de la pila
El color verde del texto es gracias a la función textcolor(10) incluida en la primera línea de código de la función principal main().
Hasta aquí todo va bien, ahora se va a alimentar el programa con la función de insertar, la cual insertará los datos numéricos en la pila.
Implementación de la función insertar() de la pila
Se define la función insertar(), no tiene parámetros y no devolverá ningún valor por eso es declarada de tipo void, esta línea de código se adiciona al código justo después de donde está definida la estructura, será el sitio dentro del código del programa para definir las tres funciones a utilizar tal como se muestra en la figura 25, una vez incluida la línea de código void insertar(void);
Figura 24 Definición de la función insertar()
El otro cambio que se hará al código inicial, es en el case 1 del swich cambiamos el mensaje a desplegar cout<<”insertar”; por insertar(); usado por el programa principal para llamar a la función insertar(). Se visualiza en figura 26.
67 Figura 25 La función insertar() en el case 1
El siguiente código contiene las instrucciones de la función insertar(), se incluirá al final del código, después de cerrar las instrucciones de la función principal main(). void insertar (void)
{
inicio=(struct pila *)malloc(sizeof(struct pila)); clrscr();
cout<<"Digite el dato de tipo ENTERO: "); cin>>inicio->numero; if (c==NULL) { c=inicio; inicio->sig=NULL; } else { inicio->sig=c; c=inicio; } }
En la figura 27 se visualiza el código de las instrucciones de la función insertar().
68 Figura 26 Instrucciones de la función insertar()
Hasta el momento se tiene definida la función insertar() al inicio del código, de igual manera se tiene todo el código de la función insertar() y la instrucción para llamar a insertar() desde la función principal en la opción 1 del case del menú de opciones. Este procedimiento se debe repetir para las otras dos funciones que faltan, es decir la función visualizar() y la función extraer().
Para probar lo hecho hasta el momento se ejecuta el código, pues ya se puede insertar los datos numéricos a la pila, pero que bueno sería si se pudiesen listar los datos insertados. Para ello incluiremos en el código en construcción la función visualizar() que es la opción 3 de el menú de opciones, se seguirán las indicaciones y los cambios al código que se llevaron a cabo para la función insertar().
Implementación de la función visualizar() de la pila
Se inicia definiendo la función para viualizar los datos ingresados, la línea de código es:
void visualizar(void); se incluye en el espacio usado para la definición de las funciones justo en la línea siguiente de donde se definió la función insertar.
Posteriormente se modifica el case 3 del swich en el menú de opciones, cambiando el mensaje a desplegar cout<<”visualizar”; por visualizar(); usado para que el programa principal haga el llamado a la función y así poder seleccionar esta opción para tener en pantalla los datos previamente insertados a la pila.
Siguiendo con la construcción de la pila queda adicionar al final del código principal la declaración completa de la función visualizar() que se incluye a continuación y que no hay código desconocido que amerite explicación alguna.
69 void visualizar (void)
{
if (c==NULL) {
clrscr();
cout<<"NO HAY ELEMENTOS A LISTAR"); } else { clrscr(); inicio=c; while (inicio!=NULL) { cout<<"Numero: "<<inicio->numero<<endl; inicio=inicio->sig; } } getch(); }
Con la inclusión de este código ya se tiene el programa con las opciones de insertar y visualizar los datos de la pila, es importante que compile el programa para depurar y corregir posibles errores. Sin embargo si siguió las instrucciones paso a paso no debe haber errores de sintaxis.
Finalmente, solo queda incluir la opción de extraer o eliminar datos de la pila con esta opción tendremos la implementación completa del programa básico para el manejo de una pila.
Manos a la obra.
Implementación de la función extraer de la pila
Como en los dos casos anteriores se siguen las mismas directrices declarando la función extraer, en el espacio utilizado para tal fin, el código es:
Void extraer(void); con esta se completa la declaración de las tres funciones como se puede observar en la figura 28.
70 Figura 27 Vista de la definición de las funciones insertar, visualizar y extraer
Se continúa modificando el case 2 del swich cambiando el mensaje a desplegar cout<<”extraer”; por extraer(); usado para que el programa principal haga el llamado a la función y así se podrá seleccionar esta opción para la eliminación del último elemento que fue insertado a la pila, se puede comprobar haciendo uso de la opción 3 del menú de opciones se visualizarán los datos que aún quedan en la pila, pero no sin antes definir al final del código principal de la pila, las instrucciones completas de la función extraer() que se incluyen a continuación. void extraer (void)
{
if (c==NULL) {
clrscr();
printf("NO HAY ELEMENTOS A ELIMINAR"); getch();
} else {
inicio=c;
cout<<"El dato a eliminar es: "<<inicio->numero; delay(1000);
c=c->sig; free(inicio); }
}
Lección 4: Análisis del código propuesto para implementar una pila
Al hacer una revisión detallada al código de la función insertar(), se encuentra la función malloc( ) tratada con detalle en la primera unidad, encargada de asignar un bloque de memoria del tamaño requerido y regresa un apuntador.
71 El valor regresado por malloc( ) ha sido asignado a inicio, un apuntador al tipo struct pila, usando el apuntador y el operador de membresía indirecta (-->) se puede accesar a los miembros de la estructura como lo indica la línea de código siguiente.
cin>>inicio->numero; se encuentra un condicional que indica que si no hay elementos en el apuntador c está en NULL, entonces se insertan datos por primera vez a la pila. El apuntador c apunta a donde apunta inicio, lo indica la línea de código c=inicio; indirectamente guardan la misma información, posteriormente se hace que el nuevo nodo apunte a NULL en la línea de código inicio->sig=NULL; Si la pila no está vacía se hace que el nuevo nodo inicio a punte a c como lo indica el código inicio->sig=c; ahora c es el nuvo nodo de la pila. Finaliza así las instrucciones de la función insertar( ).
En cuanto a la función visualizar() no amerita un análisis profundo, dado que son instrucciones básicas como un condicional que verifica si el puntero c es igual a NULL, es decir si no hay elementos en la pila, saca un mensaje indicando que la pila está vacía, en caso contrario se hace un recorrido por cada elemento de la pila visualizando su contenido de la variable apuntador inicio.
Revisando el código de la función extraer( ), se tiene que en el caso de que existan datos en la pila se guardan en inicio la ubicación de c según el código inicio=c; la instrucción cout<<"El dato a eliminar es: "<<inicio->numero; visualiza en pantalla el dato a eliminar, por medio de la función delay() se visualiza por un espacio de tiempo limitado el dato a eliminar.
Finalmente es importante que analice el código de cada función con el objetivo de que lo entienda, lo comprenda y pueda realizar las actividades propuestas, como aplicación a situaciones reales. De igual manera puede plantear cambios para optimizar el código propuesto, en caso de tener inquietudes recuerde que apelar a la bibliografía propuesta en el curso así como también puede consultar al tutor del curso.
Es importante aclarar que en esta implementación se plantea solo una de tantas