Trabajo Pilas

42
Aplicaciones de las Pilas Gabriel Amigo Gómez 1

description

Trbajo de pilas y colas

Transcript of Trabajo Pilas

Aplicacionesde las Pilas

Gabriel Amigo Gómez

1

1. DEFINICIÓN DE PILA. OPERACIONES

DEFINICIÓN: Una pila es una lista tal que las inserciones, eliminacioes y lecturas de elementos solo se pueden realizar en una posición llamada cima o tope de la pila.

Las operaciones fundamentales en una pila son:METER (en la pila): Inserta un elemento en la cima de la pila.SACAR (de la pila): Elimina el elemento que ocupaba la posición cima.

El elemento que había sido insertado inmediatamente antes que el eliminado pasa a la posición cima.

CIMA (de la pila): Devuelve el elemento que ocupa la posición cima. (Distinguir ‘CIMA’ como función sobre una pila de ‘cima’ como primera posición de una pila.)

En una pila no se puede acceder a otra posición que no sea la cima de la pila, por tanto no se puede leer o modificar una posición arbitraria de la pila, ni hacer búsquedas ni nada que implique manejar otra posición distinta de cima sin modificar la pila.Por esta razón las pilas también se conocen como listas LIFO (last in, first out), ya que el último elemento en entrar será el primero en salir.

2

2. IMPLEMENTACIÓN DE PILAS2.1 CON VECTORES2.2 CON LISTAS ENLAZADAS

2.1 PILAS USANDO VECTORES

Para implementar pilas usando vectores o arreglos es necesario saber a priori cuál va a ser el número máximo de elementos que se van a poder meter en una pila. Llamémoslo num_max.

Declaración del tipo pila (usando vectores) :

ELEMENTO=T;PILA=registro de

tope: numérico;arreglo: vector[1..num_max] de ELEMENTO;

fin registro;

tope va a corresponder a la posición en el vector del ultimo elemento insertado en la lista, o sea va a apuntar en todo momento al elemento cima. Así pila.tope=1 querrá decir que la pila está llena mientras que pila.tope=num_max + 1 querrá decir que está vacía.

Esta implementación no es apropiada cuando no se conoce el número máximo de elementos que puede contener la pila porque se podrían necesitar más posiciones de las existentes en el vector y produciría un error.Asimismo se puede desaprovechar mucha memoria si se define una pila con un vector muy grande y luego se insertan pocos elementos.La ventaja de esta implementación es la rapidez con la que se realizan las operaciones.

3

2.2 PILAS USANDO LISTAS ENLAZADAS

A diferencia de la cabecera de una lista usual, la cabecera de una pila no va a ser un registro que incluya el número de nodos y punteros al primer y último elemento, sino que va a ser simplemente un puntero que apunta al primer nodo.Por eso, abusando de notación podemos identificar una pila con su cabecera.

Declaración del tipo pila (usando listas enlazadas):

ELEMENTO=T;NODO= registro de

info: ELEMENTO;sgte: puntero a NODO;

fin registro;PILA=puntero a NODO;

4

3. IMPLEMENTACIÓN DE OPERACIONES SOBRE PILAS EN C

3.1 PILAS CON VECTORES3.2 PILAS CON L. ENLAZADAS

Esta será la implementación en C de las pilas y de las operaciones METER, SACAR, CIMA, VACÍA Y LLENA.

3.1 PILAS CON VECTORES

IMPLEMENTACIÓN DE PILA:#define tam_max 20 /*el tamaño maximo sera 20. se puede cambiar*/typedef char elemento; /*va a ser una pila de caracteres*/typedef struct {

int tope; elemento v[tam_max]; } pila;

El tipo elemento variará dependiendo del programa, así como tam_max.

OPERACIONES:VACÍA:int vacia(pila1)pila pila1;{

if(pila1.tope==tam_max){return 1;}else{return 0;}

}/*FIN VACIA*/Esta función devuelve 1 si la pila está vacía y 0 si no lo está.

LLENA:int llena(pila1)pila pila1;{

if((pila1.tope)==0){return 1;

5

}else{return 0;}

}/*FIN LLENA*/Esta función devuelve 1 si la pila está llena y 0 si no lo está.

SACAR:int sacar(punt_pila)pila *punt_pila;{

int vacia();int valor_vacia;valor_vacia=vacia(*punt_pila);if(valor_vacia==1){

return 0;}else{

(*punt_pila).tope=(*punt_pila).tope+1;return 1;

}}/*FIN SACAR*/Esta función elimina el elemento cima de la pila y devuelve 1 si la pila no

está vacía y simplemente devuelve 0 si la pila está vacía. (Aplicar SACAR a

una pila vacía la deja igual).

METER:int meter(lo_que_meto,punt_pila)elemento lo_que_meto;pila *punt_pila;{

int llena();int copia_tope;int valor_llena;valor_llena=llena(*punt_pila);if(valor_llena==1){return 0;}else{copia_tope=(*punt_pila).tope;(*punt_pila).v[copia_tope-1]=lo_que_meto;

6

(*punt_pila).tope=copia_tope-1;return 1;}

}/*FIN METER*/Esta función mete el elemento lo_que_meto en la pila y devuelve 1 si no

está vacía y simplemente devuelve 0 si está llena.

CIMA:elemento cima(pila1) /*1º hay q asegurarse d q no esté vacía*/pila pila1;{int vacia();int copia_tope;if(vacia(pila1)==0){ /*si no esta vacia se procede*/

copia_tope=pila1.tope;return pila1.v[copia_tope];}

else{printf("\n Error. Has aplicado la funcion cima a una pila vacia\n");return pila1.v[1];}/*esto ultimo es absurdo, pues la pila esta vacia, pero te

ahorra un warning al compilar*/}/*FIN CIMA*/Esta función devuelve el elemento cima si la pila no está vacía y devuelve un elemento de tipo elemento si está vacía dando un mensaje de error. Por eso hay que asegurarse de que la pila no está vacía antes de llamar a CIMA

7

3.2 PILAS CON L. ENLAZADAS

IMPLEMENTACIÓN DE PILA:typedef char elemento;/*defino elemento como char. se puede cambiar*/typedef struct NODE{ /*defino el tipo nodo como registro de info y sgte*/

elemento info; /*en ppio las pilas seran de char, pero esto se puede cambiar*/

struct NODE *sgte;};

typedef struct NODE nodo;typedef nodo *pila; /*defino el tipo pila como puntero a nodo*/

El tipo elemento variará según el programa.

OPERACIONES:VACÍA:int vacia(pila1)pila pila1;{

if (pila1==NULL) return 1;else return 0;

}/*FIN VACIA*/Vacía devuelve 1 si la pila está vacía y 0 si no lo está.

SACAR:int sacar(punt_pila)pila *punt_pila;{int vacia();pila aux_pila;

if(vacia(*punt_pila)==1){return 0;}

else{ /* (*punt_pila)es igual a pila1, q es un puntero al 1er nodo, luego quiero modificar su contenido: */

aux_pila=(*(*punt_pila)).sgte;free(*punt_pila);*punt_pila=aux_pila;return 1;}

}/*FIN SACAR*/

8

Sacar saca un elemento de la pila si no está vacía devolviendo 1 o devuelve 0 si está vacía.

METER:int meter(lo_que_meto,punt_pila)elemento lo_que_meto;pila *punt_pila;{pila aux_pila;aux_pila=*punt_pila; /*asi me hago una copia de pila1 en aux_pila*/*punt_pila=(nodo*)calloc(1,sizeof(nodo)); /* pila1=*punt_pila */

if(*punt_pila==NULL){printf("\n Error. No se puede reservar mas espacio.");return 0;}else{ /*se ha podido reservar espacio para un nodo: se procede a meter*/(*(*punt_pila)).info=lo_que_meto; /* (**punt_pila) apunta al 1er nodo*/(*(*punt_pila)).sgte=aux_pila; /*para q el 1er nodo apunte al q ahora es el

2º*/return 1;}

}/*FIN METER*/Meter mete lo_que_meto en la pila en caso de que no esté llena devolviendo un 1 o da un mensaje de error y devuelve un 0 en caso de que no haya más espacio en memoria para otro nodo.

CIMA:elemento cima(pila1)pila pila1;{int vacia();if(vacia(pila1)==0){/*no vacia*/

return ((*pila1).info);}else{

printf("\n Error. Has aplicado la funcion cima a una pila vacia\n");return ((*pila1).info);}/*esto ultimo es absurdo, pues la pila esta vacia, pero

te ahorra un warning al compilar*/} /*FIN VACÍA*/Cima devuelve el elemento cima de una pila no vacía. Si se aplica sobre una función vacía el programa aborta, así que hay que asegurarse antes de ejecutarla.

9

4. ELIMINACIÓN DE LA RECURSIÓN. TORRES DE HANOI.Cuando se hace una llamada a una función el compilador debe guardar en alguna parte el valor que tienen todas las variables antes de entrar en la función, pues de lo contrario estos valores podrían ser sobreescritos.Cada colección de variables que se guardan antes de hacer una llamada se llama registro de activación.

Ejemplo:El factorial recursivo:

main(){int n, factorial();scanf(“%d”,&n);printf(“Este es el factorial de %d: %d”,n,factorial(n));}

int factorial(int n){if(n==1) return 1; /*caso base*/else return (n*factorial(n-1));}

Supongamos que le damos inicialmente n=3. Al entrar por primera vez en factorial es necesario que guarde ese 3 en alguna parte. Lo va a guardar en un registro de activación.Como la información que hay en un registro de activación no va a poder ser usada hasta que la llamada correspondiente haya terminado, la estructura de datos pila es muy adecuada para almacenar los registros de activación:Según se van haciendo llamadas, se meten registros de activación en la pila de forma que los registros de activación correspondientes a llamadas más internas estarán más arriba en la pila que los correspondientes a llamadas más externas.

Ejemplo:Factorial de 3.

Partimos de una pila de registros de activación vacía y una variable entera auxiliar que vamos a llamar RF (registro de la función) y que va a ir guardando los sucesivos valores provisionales de la función factorial.Llamamos por primera vez a factorial() con n=3, luego metemos un 3 en la pila (figura b). Como no es el caso base se vuelve a llamar a factorial() con n=2. Se mete un 2 en la pila (figura c). A continuación se llama a factorial() con n=1 (figura d). En este punto llegamos a “return 1”, luego metemos un

10

1 en RF y sacamos un registro de la pila (pues la función factotrial(1) ya ha terminado) (figura e).

Se multiplicará entonces el 2 de la cima de la pila por RF (que no va a ser otra cosa que factorial(1), almacenando el resultado en RF y sacando después el 2 de la pila (figura f).A continuación se multiplica el 3 de la cima por RF (factorial(2)) quedando la pila vacía y un 6 en RF (figura g). Entonces se devuelve el control al programa principal quedando la pila vacía y un 6 en RF (figura h).

ELIMINACIÓN DE LA RECURSIÓN DE COLA.Se dice que un algoritmo recursivo presenta recursión de cola o tail recursion cuando la llamada recursiva tiene lugar en la última línea del algoritmo.Ejemplo:

void funcion_recursiva(param_1,....,param_m) {declaración de variables;

if (caso base){sentencia_base_1;....sentencia_base_r;

}else{

sentencia 1;....sentencia n;funcion_recursiva(param_1’,....,param_m’); ←última línea

}}

11

funcion_recursiva presenta recursión de cola.En el caso de que la recursión sea recursión de cola, es fácil de eliminar, pues equivale a un esquema while:

void funcion_recursiva(param_1,....,param_m) {declaración de variables;

while (no se satisfacen las condiciones del caso base){sentencia 1;....sentencia n;

}sentencia_base_1;....sentencia_base_r;}

TORRES DE HANOI ELIMINANDO LA RECURSIÓN.El algoritmo recursivo que habíamos visto en clase para el problema de las torres de Hanoi es el siguiente:

void mover( int n, char O, char A, char D){/*va a mover de 'O' a 'D' */if(n==1) { /* caso basico */printf ("\n mover de %c a %c", O,D);}else { mover( n-1,O ,D, A);

printf ("\n mover de %c a %c", O,D); mover (n-1, A, O, D);}

}

Cada vez que se llama a mover, si no estamos en el caso base, mover va a llamarse otras dos veces, haciendo un printf entre estas dos llamadas.El siguiente esquema muestra las llamadas a mover si partimos de 2 discos:

12

Vemos que resulta un árbol binario.

Los argumentos de la función mover son: la altura de la torre a considerar (n) y las letras que corresponden en cada momento a las torres origen, auxiliar y destino (‘O’,’A’,’D’). Por tanto estos cuatro datos han de ser guardados en un registro de activación cada vez que se hace una nueva llamada a mover.Asimismo hay que guardar el número de llamadas que ha hecho mover (0, 1 ó 2), pues en función de esto se llama otra vez a mover o se devuelve el control a la función anterior. Lo guardaremos en una variable entera num_hijos.

Luego cada registro de activación va a contener dos variables enteras y tres de tipo carácter: altura, num_hijos, caracter1, caracter2, caracter3.

El algoritmo que elimina la recursión del algoritmo original va a recorrer ordenadamente el árbol metiendo registros de activación en una pila a medida que se profundiza en el árbol y sacándolos a medida que se sube a un nivel más próximo a la raíz. A la vez que hace esto, muestra por pantalla los movimientos correspondientes. El algoritmo termina cuando la pila se queda vacía, lo que significa que se ha devuelto el control al programa principal.

El recorrer ordenadamente el árbol es un proceso iterativo, con lo que la recursión queda eliminada.

La implementación de pila más adecuada es con vectores porque si partimos de una torre con n discos, al llegar al caso base la pila tendrá n registros de activación y será el momento en el que más llena esté.De esta forma definiendo la pila con un vector de n posiciones no nos quedaremos sin espacio.

13

5. AJUSTE DE PARÉNTESIS, CORCHETES Y LLAVESOtra aplicación de las pilas consiste en la revisión de programas para buscar errores.Por ejemplo en C todo paréntesis, llave o corchete de apertura debe tener su correspondiente símbolo de clausura Así una expresión con 10 paréntesis de apertura y 9 de clausura no puede ser válida. Tampoco se admiten expresiones de este estilo: ( [ ) ] , { [ } ].Se puede realizar un programa que, dado un fichero en C, nos diga si los paréntesis, corchetes y llaves están bien anidados.

El algoritmo funciona así: Partimos de una pila de caracteres vacía. Se leen caracteres del fichero uno a uno y según lo que sean, así se tratan:a) Si leemos un caracter que no sea un paréntesis, un corchete o una llave entonces no hacemos nada y pasamos al siguiente carácter.b) Si el caracter es uno de apertura: ( , [ ó { entonces lo metemos en la pila.c) Si el carácter es uno de clausura: ), ] ó } miramos la cima de la pila y si es el correspondiente símbolo de apertura, se saca de la pila y pasamos al siguiente carácter. Si la cima no fuese el símbolo de apertura correspondiente, entonces se mete el carácter en la pila.De esta forma, al terminar de leer el fichero sabremos que los paréntesis, corchetes y llaves están bien anidados si y sólo si la pila ha quedado vacía.

Este algoritmo tiene un inconveniente: los paréntesis, corchetes y llaves deben estar PERFECTAMENTE anidados, de forma que un comentario de este estilo provocaría que un trozo de código bien hecho se interpretase como un trozo con errores: /*este comentario se carga el programa: ( [ ) ]*///este comentario también {{De la igual forma una expresión como esta también sería mal interpretada:printf(“Esto esta bien pero dara fallo: %c”,’)’);

Luego hay que asegurarse de que al programa que encuentra errores se le pase un fichero sin comentarios (o bien hechos) y sin utilizar los símbolos (, ), [, ], {, } como caracteres (o utilizados de forma que no den error).

La implementación más lógica de pila para este programa es la que usa listas enlazadas, pues la longitud del fichero puede ser muy variable y una implementación con vectores podría quedarse corta o pasarse de larga.

14

6. EVALUACIÓN DE EXPRESIONES EN NOTACIÓN POSFIJA

LA NOTACIÓN POSFIJA:1+(2*(9-3)) una expresión con la notación usual o infija.La notación posfija es otra notación en la cuál no existen paréntesis ni jerarquía en las operaciones. La expresión anterior en notación posfija sería así:9 3 - 2 * 1 +Quiere decir lo siguiente: el primer operador es la resta, luego va a restar los dos números anteriores: 9 y 3. Entonces el bloque 9 3 – sería equivalente a un 6:6 2 * 1 +El siguiente operador es un *, luego se multiplican los dos números anteriores: 6 y 2. Entonces el bloque 6 2 * sería equivalente a 12:12 1 +El siguiente operador es +, luego se suman los dos números anteriores:12 y 1.Entonces el resultado es 13. Vemos que hacemos lo mismo que haríamos en notación infija y no hemos tenido que acudir a paréntesis ni a jerarquía de operaciones. Por eso la evaluación de expresiones posfijas es muy fácil de implementar.

Notación infija Notación posfija2*(3+8/(6-4)) 2 3 8 6 4 - / + *

3+2*7-1 3 2 7 * + 1 -1*2*3*4+5 1 2 * 3 * 4 * 5 +

ejemplos de expresionesEVALUACIÓN DE EXPRESIONES EN NOTACIÓN POSFIJA:Otra aplicación de las pilas es la evaluación de expresiones en notación posfija:

Disponemos de una expresión en notación posfija. Supongamos que, por ejemplo, está almacenada en un fichero. Usaremos una pila de reales.

El algoritmo que evalúa la expresión hará lo siguiente: Leemos los datos del fichero uno a uno y los tratamos según su tipo:

a) Es un real. Lo metemos en la pila.b) Es un operador. Sacamos 2 números reales de la pila y aplicamos el

operador sobre ellos. Metemos el resultado en la pila.(Si la operación es – ó / entonces el elemento que antes hubiese entrado en la pila será el que aparezca a la izquierda del operador.

15

Por ejemplo, si tenemos la expresión 7 5 -, el 7 entra antes que el 5 en la pila, luego se hará la operación 7-5 y no la 5-7.)

En resumen, se trata de recorrer el fichero y cada vez que encontremos un operador se lo aplicamos a los dos números anteriores hasta que se termina el fichero. Entonces el nº real que quede en la pila será el resultado.

Es difícil encontrarse con expresiones aritméticas muy largas así que usando la implementación de pila con vectores con un vector de unas 20 posiciones no es probable encontrar errores, pero si se manejan expresiones muy largas la implementación de listas enlazadas dará más seguridad.

16

7. CONVERSIÓN DE NOTACIÓN INFIJA A POSFIJASe pude transformar una expresión con notación infija en su correspondiente con notación posfija utilizando pilas para ello.

Introduciremos una expresión en notación infija y queremos escribir su equivalente posfija en un fichero destino, por ejemplo.

El algoritmo que hace esto funciona así:Tenemos una pila de caracteres, inicialmente vacía, en la que vamos a meter los operadores.

Pedimos números y operadores de uno en uno y según su tipo así los tratamos:

a) Es un nº: Lo escribimos directamente en el fichero destino.b) Es un operador: Si la pila está vacía, lo metemos en la pila.

Si la pila no está vacía y la cima de la pila es un operador de menor prioridad, entonces también lo metemos en la pila.Si la cima de la pila es un operador de mayor o igual prioridad, entonces sacamos operadores de la pila y los escribimos en el fichero destino hasta que en la cima haya un operador de menor prioridad o un paréntesis de apertura o hasta que la pila quede vacía. Entonces metemos el operador en la pila.De esta forma nos aseguramos de que las operaciones con mayor prioridad se realizan anteriormente, pues sus operadores son los que antes salen de la pila.

c) Es un paréntesis:-Es de apertura: se mete en la pila. Sólo se puede sacar de la pila cuando se encuentra un ‘)’. -Es de clausura: se van sacando operadores de la pila y se escriben en el fichero hasta que se encuentra ‘(‘. Entonces se saca ‘(‘ y se continúa el proceso.De esta forma aseguramos que las operaciones contenidas en un paréntesis van a ser agrupadas.

Al terminar este proceso sacamos uno a uno los operadores que pudieran quedar en la pila y los escribimos en el fichero destino.

Al igual que en la aplicación anterior, dependiendo de la longitud de las expresiones que se vayan a manejar, convendrá utilizar una u otra implementación de pila.

17

Ejemplo:

Tenemos la expresión 3-36/(5+1)El primer dato es un 3, así que lo mete en el fichero. Luego encuentra un -. Va quedando así:Pila Fichero de salida- 3Ahora lee un 36 y lo escribe en el fichero. Al leer /, mira la cima de la pila. Como la cima de la pila es un -, que es de menor prioridad, mete el / en la pila: Pila Fichero de salida/ 3 36-Después lee un ‘(‘, que siempre se mete en la pila. Luego lee un 5 y lo escribe en el fichero:Pila Fichero de salida( 3 36 5/-Ahora encuentra un +. Como la cima de la pila es ‘(‘, que solo puede ser sacado al encontrar ‘)’, mete el + en la pila.Entonces lee 1 y lo escribe en el fichero:Pila Fichero de salida+ 3 36 5 1(/-Encuentra un ‘)’ luego va sacando operadores de la pila hasta encontrar un ‘(‘: Saca + y lo escribe en el fichero y luego saca ‘)’Pila Fichero de salida/ 3 36 5 1 +-Ya no quedan elementos que leer así que saca los operadores que quedaban en la pila de uno en uno y los escribe en el fichero:Pila Fichero de salida

3 36 5 1 + / -

Combinando esta aplicación con la anterior, tenemos una calculadora básica que admite el uso de paréntesis y respeta la jerarquía de operaciones.

18

TORRES DE HANOI EN C:/*TORRES DE HANOI ELIMINANDO RECURSIÓN*/#include<stdio.h>#define tam_max 20

typedef struct{int altura; /*altura de la torre a considerar*/int num_hijos; /*nº de hijos a los que ha llamado la función recursiva*/char caracter1,caracter2,caracter3;

} elemento;/*cada elemento de la pila va a ser un registro elemento*/

/*defino el tipo pila usando vectores:*/typedef struct {

int tope; elemento v[tam_max];} pila;

main(){int n,vacia(),sacar(), meter();pila pila1, *punt_pila;elemento registro_aux, cima(), crear_registro();pila1.tope=tam_max;punt_pila=&pila1;

printf(" TORRES DE HANOI ELIMINANDO LA RECURSION\n");do{printf("\n Dame el numero de discos de la torre: ");scanf("%d",&n);if (n<=1) printf("\n Debe ser mayor que 1\n");}while(n<=1);

registro_aux.altura=n; /*el primer elemento de la pila va a ser siempre este*/registro_aux.num_hijos=0;registro_aux.caracter1='O';registro_aux.caracter2='A';registro_aux.caracter3='D';meter(registro_aux,punt_pila);/*ahora entro en un bucle que, dependiendo de lo que haya en la cimade la pila hace una cosa u otra*/

while(vacia(pila1)==0){ if(cima(pila1).altura==1){/*llego al caso base*/

printf("\n Mover de %c a %c\n",cima(pila1).caracter1,cima(pila1).caracter3); sacar(punt_pila);

} else{/*altura>1*/

switch(cima(pila1).num_hijos){ case 2:{/*las 2 llamadas correspondientes a este nodo están hechas*/

sacar(punt_pila);

19

};break; case 1:{

printf("\n Mover de %c a %c\n",cima(pila1).caracter1,cima(pila1).caracter3);

registro_aux=cima(pila1); sacar(punt_pila); registro_aux.num_hijos=registro_aux.num_hijos+1;/*como voy a meter

algo en la pila, he llamado a otro hijo*/ meter(registro_aux,punt_pila); /*las 4 últimas líneas son solo para sumar 1 a num_hijos*/ meter(crear_registro(cima(pila1)),punt_pila);

};break; case 0:{

registro_aux=cima(pila1); sacar(punt_pila); registro_aux.num_hijos=registro_aux.num_hijos+1;/*como voy a meter

algo en la pila, he llamado a otro hijo*/ meter(registro_aux,punt_pila); /*las 4 últimas líneas son solo para sumar 1 a num_hijos*/ meter(crear_registro(cima(pila1)),punt_pila);

};break; }/*fin switch*/

} /*fin if*/}/*fin while*/printf("\n");}/************FIN MAIN************/

elemento crear_registro(registro_anterior)elemento registro_anterior;{elemento registro_creado;registro_creado.altura=registro_anterior.altura-1;registro_creado.num_hijos=0;/*como lo creo, aún no tiene hijos*/if(registro_anterior.num_hijos==1){/*esto quiere decir que aun no tiene

hijos y que el que vamos a crear es el primero*/registro_creado.caracter1=registro_anterior.caracter1;registro_creado.caracter2=registro_anterior.caracter3;registro_creado.caracter3=registro_anterior.caracter2;}

else{/*ya ha llamado a un hijo y vamos a crear el 2º y a meterlo en la pila*/registro_creado.caracter1=registro_anterior.caracter2;registro_creado.caracter2=registro_anterior.caracter1;registro_creado.caracter3=registro_anterior.caracter3;}

return registro_creado;}/*FIN CREAR_REGISTRO*/

20

DEPURADOR DE ERRORES EN C:/*PROGRAMA QUE ENCUENTRA ERRORES EN UN PROGRAMA EN C USANDO PILAS*//*CON LISTAS ENLAZADAS*/#include<stdio.h>#include<stdlib.h>typedef struct{ /*va a ser una pila de registros: cada en nodo.info*/

char alfanumerico;/*habrá un registro de caracter y entero q indicará*/int su_linea; /*el parentesis, corchete o llave y su linea en el fichero*/} elemento;

elemento;typedef struct NODE{

elemento info;struct NODE *sgte;};

typedef struct NODE nodo;typedef nodo *pila;

/*****MAIN*****/main(){int vacia();int sacar();int meter();elemento cima();void trata_caracter();pila pila1;pila *punt_pila;FILE *f;char nombre_f[20];char caracter;int linea; /*indica en qué linea del fichero estamos*/

pila1=NULL;/*pila1 empieza siendo vacia*/punt_pila=&pila1;

printf("\n Dame un fichero en c y te digo si tiene errores: ");gets(nombre_f);f=fopen(nombre_f,"r");linea=1;while((caracter=getc(f))!=EOF){

trata_caracter(caracter,linea,punt_pila);if(caracter=='\n') linea=linea+1;}/*este bucle termina cuando se llega a EOF*/

fclose(f);/*aqui se supone q ya se ha leido todo el fichero y dispongo d la pila*/

if(vacia(pila1)==1){ /*termino el fichero y la pila esta vacia*/printf("\n Los parentesis, corchetes y llaves estan bien anidados:\n Tu programa

esta aparentemente bien hecho.\n");}

21

else{/*termino el fichero y la pila no esta vacia*/ printf("\n"); do{ /*el siguiente printf lo va a hacer siempre sobre una pila no vacia ===>

puedo usar CIMA*/ printf(" El caracter %c situado en la linea %d no esta bien anidado.\

n",cima(pila1).alfanumerico,cima(pila1).su_linea); sacar(punt_pila); /*una vez muestro los resultados por pantalla voy vaciando la

pila*/ }while(vacia(pila1)==0); printf(" Revisa tu programa.\n\n");}

}/****FIN MAIN****/

void trata_caracter(caracter,linea,punt_pila)char caracter;int linea;pila *punt_pila;{int vacia();int meter();int sacar();elemento cima();elemento registro;

registro.alfanumerico=caracter;registro.su_linea=linea;

if(caracter=='(' || caracter=='[' || caracter=='{'){meter(registro,punt_pila);

}else{ /*el caracter no es de apertura*/

switch(caracter){case ')': /* nos encontramos con ')'*/

if (vacia(*punt_pila)==1){ /*pila vacia*/meter(registro,punt_pila);}

else{ /*pila no vacia*/if((cima(*punt_pila)).alfanumerico=='('){

sacar(punt_pila);}else{

meter(registro,punt_pila);}};break;

case ']': /*nos encontramos con ']'*/if (vacia(*punt_pila)==1){ /*pila vacia*/

meter(registro,punt_pila);}else{ /*pila no vacia*/

if((cima(*punt_pila)).alfanumerico=='['){sacar(punt_pila);}

22

else{meter(registro,punt_pila);}

};break;

case '}': /*nos encontramos con '}'*/if (vacia(*punt_pila)==1){ /*pila vacia*/

meter(registro,punt_pila);}else{ /*pila no vacia*/

if((cima(*punt_pila)).alfanumerico=='{'){sacar(punt_pila);}

else{meter(registro,punt_pila);}

};break;}

}}/*FIN TRATA_CARACTER*/

23

CONVERSIÓN DE EXPRESIONES DE NOTACIÓN INFIJA A POSFIJA EN C:/*ESTE PROGRAMA ES UNA CALCULADORA BÁSICA.SE INTRODUCEN NÚMEROS Y OPERADORES UNO POR UNO COMO EN UNA CALCULADORA.DESPUÉS SE PASA A NOTACIÓN POSFIJA Y SE GUARDA EN EN UN FICHERO.SI SE QUIERE SE EVALÚA LA EXPRESIÓN Y SE DA UN RESULTADO

Las pilas tienen la implementación de listas enlazadas*/#include<stdio.h>#include<stdlib.h>

typedef char elemento;typedef struct NODE{

elemento info; /*pila de caracteres (pila de operadores y parentesis)*/struct NODE *sgte;};

typedef struct NODE nodo;typedef nodo *pila;

main(){void posfija();float calcula();char nombre_f[20];

printf(" Este programa transforma una expresion con notacion infija\n en su correspondiente expresion con notacion posfija\n");printf("\n Dame el nombre del fichero en el que guardar los datos: ");gets(nombre_f);posfija(nombre_f);printf("\n Tu expresion en notacion posfija se ha almacenado en el fichero %s\n\n",nombre_f);}/************FIN MAIN************/

void posfija(nombre_f)/*POSFIJA*/char nombre_f[20];{int vacia(),meter(),sacar(),tipo(),strlen(),tipo_dato,long_dato;/*long_dato da la longitud de la cadena dato*/elemento cima(),cima_aux;pila pila1;pila *punt_pila;char dato[12];FILE *f;

24

pila1=NULL;punt_pila=&pila1;

f=fopen(nombre_f,"w");

do{printf("\n Introduce el dato o el operador.\n Introduce '=' si quieres terminar la

expresion: ");gets(dato);tipo_dato=tipo(dato[0]);/*con esto identifico el dato que me han dadoy segun lo que valga lo trataré de una forma u otra*/

switch(tipo_dato){case 1:{ /*es un real*/

long_dato=strlen(dato);fprintf(f," %s",dato);

};break;

case 5:{ /*es un ')', luego la pila no está vacia y voy a sacar elementos*/do{

fprintf(f," %c",cima(pila1));sacar(punt_pila);/*no hay riesgo d que este vacia pues debe haber un '(' al menos*/if(vacia(pila1)==1){

cima_aux='(';}/*asi sale del bucle sin aplicar cima a un apila vacia*/

else cima_aux=cima(pila1);}while(cima_aux!='(');/*finalizado este bucle, tenemos '(' en la cima de la pila. hay q sacarlo:*/sacar(punt_pila);

};break;

case 6:{/*es un '=' =====> no hago nada*/};break;

default:{/* +,-,*,/,( */ if(vacia(pila1)==1){/*pila vacía*/

meter(dato[0],punt_pila);}else{

cima_aux=cima(pila1);/*no problemo xq pila1 no es vacia*/while(vacia(pila1)!=1 && tipo(cima_aux)>=tipo(dato[0]) &&

tipo(cima(pila1))!=4){fprintf(f," %c",cima(pila1));sacar(punt_pila);

if(vacia(pila1)!=1) cima_aux=cima(pila1);/*si pila1 esta vacia dejamos cima_aux igual xq no va a entrar en el bucle de todas maneras*/

25

}meter(dato[0],punt_pila);}

}}/*fin del switch*/}while(dato[0]!='=');/*he terminado de pedir los datos.ahora solo me queda sacar todo lo que queda en la pila:*/

while(vacia(pila1)!=1){/*ahora si no está vacía la vacío yo*/fprintf(f," %c",cima(pila1));sacar(punt_pila);}fclose(f);}/*FIN POSFIJA*/

int tipo(caracter)/*esta función me devuelve un entero en funcion de*/char caracter;/*lo que simbolice el caracter leido: real==>0, +ó- ==>1, etc.*/

{if((int)caracter>=48 && (int)caracter<=57){/*si el codigo ascii del 1er

caracter corresp a un entero entonces sé q va a ser un nº entre 48 y 57*/return 1;}

else{if(caracter=='+' || caracter=='-'){

return 2;}else{

if(caracter=='*' || caracter=='/'){return 3;}

else{if(caracter=='('){

return 4;}else {

if(caracter==')'){return 5;}

else {if(caracter=='='){

return 6;}else return 0;

}/*tipo devuelve 0 si es un caracter no válido*/

}}

}}

}/*FIN TIPO*/

26

EVALUADOR DE EXPRESIONES EN NOTACIÓN POSFIJA EN C:/*Las pilas tienen la implementación de listas enlazadasHay que pasarle un fichero creado por el algoritmo anterior*/#include<stdio.h>#include<stdlib.h>

typedef double elemento;typedef struct NODE{

elemento info; /*pila de reales*/struct NODE *sgte;};

typedef struct NODE nodo;typedef nodo *pila;

main(){char nombre_f[20];double calcula();

printf("\n Necesito un fichero creado por el algoritmo POSFIJA.\n");printf("\n Dame el nombre del fichero: ");gets(nombre_f);printf("\n El valor de la expresion que me has pasado es %f\n\n",calcula(nombre_f));}/********FIN MAIN********/

/*la funcion calcula va cogiendo cadenas de caracteres delimitadas por espacios */double calcula(nombre_f)/*CALCULA*/char nombre_f[20];{int vacia(),sacar(),meter(),tipo();double atof();/*atof transforma una cadena de caracteres en un real*/elemento cima();pila pila1,*punt_pila;char caracter,dato[12];double real_aux1,real_aux2;FILE *f;

pila1=NULL;punt_pila=&pila1;

f=fopen(nombre_f,"r");while(caracter=getc(f)!=EOF){/*tras esta asignacion, caracter deberia valer ' '*/ fscanf(f,"%s",dato); switch(tipo(dato[0])){

case 1:{/*tengo en dato una cadena q representa un real*/meter(atof(dato),punt_pila);/*meto el real en la pila*/

};break;

27

default:{/*se trata de un operador*/real_aux1=cima(pila1);sacar(punt_pila);real_aux2=cima(pila1);/*real_aux2 entro antes en la pila q real_aux1*/sacar(punt_pila);if(dato[0]=='+') meter(real_aux2+real_aux1,punt_pila);else{

if(dato[0]=='-') meter(real_aux2-real_aux1,punt_pila);else{

if(dato[0]=='*') meter(real_aux2*real_aux1,punt_pila);else /*tenemos un '/'*/

meter(real_aux2/real_aux1,punt_pila);}

}}

}/*fin switch*/}/*fin while*/fclose(f);return cima(pila1);}/*FIN CALCULA*/

int tipo(caracter)/*esta función me devuelve un entero en funcion de*/char caracter;/*lo que simbolice el caracter leido: real==>0, +ó- ==>1, etc.*/

{if((int)caracter>=48 && (int)caracter<=57){/*si el codigo ascii del 1er

caracter corresp a un entero entonces sé q va a ser un nº entre 48 y 57*/return 1;}

else{if(caracter=='+' || caracter=='-'){

return 2;}else{

if(caracter=='*' || caracter=='/'){return 3;}

else{if(caracter=='('){

return 4;}else {

if(caracter==')'){return 5;}

else {if(caracter=='='){

return 6;}else return 0;}

/*tipo devuelve 0 si es un caracter no válido*/}

}}

}}/*FIN TIPO*/

28

Bibliografía:

LIBROS:Weiss.Aho, Hopcroft, Ullman.

SITIOS:http://www.cse.ucsc.edu/~elm/Classes/12b/w02/schedule.shtmlhttp://www.programacion.nethttp://campusvirtual.uma.es/emetprog/LABORATORIO/matdocLP/teoria/doc/LP_curso_0304.Tema_7_Recursividad.pdf

29