2.2. Stack (Pila LIFO) Un stack es una estructura o conjunto de nodos en la cual puede insertarse o eliminarse sólo por uno de los extremos, que es la cabeza. Las operaciones que se pueden realizar en un stack son: a. Agregar un elemento. b. elemento. Figura 2.3 Ejemplo de un Stack secuencial. Eliminar un Cada nodo que se va a agregar o eliminar del stack tiene una capacidad de C bytes. Algoritmos1 de agregar y eliminar un nodo en un stack secuencial. ADDSTACKS(stack,top,base,max,dato) SI stack = null TH stack,base=top info.top <- dato SN SI (top-base<max) TH top <- top + C info.top <- dato SN "el stack esta lleno" FSI FSI FINADDSTACKS() SUBSTACKS(stack,top,base,max,dato) SI stack = null TH "no existe el stack" SN SI (top >= base) TH info.top -> dato top <- top - C SI (top<base) TH stack,base,top=null SN "el stack esta vacio" FSI FSI FINSUBSTACKS() Como puede darse cuenta el lector, no se ha perdido en ningún momento la información que existe en algún nodo del stack Ejemplo. Se cuenta con un stack secuencial, STACK1, se requiere determinar si algún nodo tiene la información que esta en datox, si es así, debe cambiarse por la que hay en infox. 1 En los algoritmos de este documento existen errores, aunque se ha revisado cuidadosamente para que no suceda, sin embargo, las continuas modificaciones conducen a que los cambios no se realicen en toda parte y, por tanto algunos algoritmos quedan con una versión anterior. Los algoritmos se presentan en seudocódigo. ALG() // se considerara un stack adicional para guardar temporalmente la información SI STACK1 = null TH "el stack no existe" SN SI top1=base1 TH "el stack esta vacio" SN w=0 MQ (top1 > base1 & w=0) top1 <- top1 - C SI info.top1=datox TH dato<-info.top1 info.top1<-infox top1<-top1+C w=1 SN SI stack2=null TH stack2,base2,top2 FSI info.top2 <- info.top1 top2 <- top2+C FSI FMQ MQ (top2 > base2) top2 <- top2 -C info.top1 <- info.top2 top1 <- top1 + C FMQ FSI FSI FINALG() 26 Ejemplo. Se tiene un conjunto de stack secuenciales continuos, se ha llenado el stack i, hacer el algoritmo para hacer los corrimientos necesarios a fin de dar espacio en el stack i. Alg(G) sum = lf-li PARA j=1, n top(j)=oldtop(j) FPARA inc = 0 PARA j = 1, n sum = sum - [top(j) - base(j)] FPARA inc = |sum/n| PARA i=1, n oldtop(i) = oldtop(i) + inc FPARA PARA j=1, n SI top(j)>oldtop(j) TH d(j) = top(j) - oldtop(j) inc = inc + d(j) SN d(j) = 0 FSI FPARA SI sum = 0 TH SATURA SN alf = (k * (sum / c)) / n bet = ((1 - k) * (sum/c)) / inc nbase(1) = base (1) PARA j = 2, n Luis Carlos Torres Soler Estructuras de Datos nbase(j) = nbase(j-1) + [top(j-1) - base(j-1)] + alf*c + bet*c * d(j-1)] 27 FPARA FSI finalg() Ejercicio Sea que en el stack(i) se presento una condición de OVERFLOW al intentar insertar un nuevo elemento, por lo tanto después de averiguar en que stack posterior (stack i+1,i+2, hay espacio disponible, se empieza a desplazar hasta darle área al stack i. Puede buscarse en los stack i-1,i-2,.. Alg(M) Adelan: PARA l = i+1, n SI top(l)-base(l+1)>K*C TH PARA m=top(l)+C-1 a base(i+1) info(m+c) = info(m) FPARA PARA m = i+1, l base(m) = base(m) + c top(m) = top(m) + c FPARA INSERTAR FSI FPARA finadelan Figura 2.5 Ejemplo de un stack encadenado. Atras: PARA l = i-1, 1 SI top(k) <base(k+1) TH PARA m=base(l+1)+c, top(i) info(m-c) = info(m) FPARA PARA m = l+1, i base(m) = base(m)-c top(m) = top(m) - c FPARA INSERTAR FSI FPARA finatras Facultad de Ingeniería 28 SATURADO Fin_alg() Figura 2.6 Agregar un nodo al Stack encadenado. Cuando el stack es encadenado, cambia en general la nomenclatura, indicando adecuadamente en que dirección se encuentra la información. Algoritmos de agregar y eliminar nodos en un stack encadenado ADDSTACKE(stack,dato) Figura 2.7 Eliminar un nodo del Stack encadenado. info.nodo <- dato link.nodo <- null SI stack = null TH stack <- nodo SN link.nodo <- stack stack <- nodo FSI FINADDSTACKE() SUB STACKE(stack,dato) SI stack = null TH "no existe el stack" SN info.stack -> dato stack <- link.stack FSI SUBSTACKE() 2.3. Cola (Pila FIFO) Una cola es una estructura o conjunto de nodos en la cual puede insertarse solamente por la cola y puede borrarse por la cabeza. Figura 2.8. Ejemplo de una cola secuencial. Las operaciones que se pueden realizar en una cola son: a. Agregar un nodo. b. Eliminar un nodo. Luis Carlos Torres Soler Estructuras de Datos 29 Algoritmos de agregar y eliminar un nodo en una cola secuencial. ADDCOLAS(cola,col,cab,dato) SI cola = null TH cola,col=cab info.col <- dato SN SI (cab-col < N) TH col <- col + C info.col <- dato SN "la cola esta llena" FSI FSI FINADDCOLAS() SUBCOLAS(cola,col,cab,dato) SI cola = null TH "la cola no existe" SN SI (cab-col > 0) TH info.cab -> dato cab <- cab + C cola <- cab SI (cab < col) TH cola=null FSI SN "la cola esta vacia" FSI FSI FINSUBCOLAS() En las colas encadenadas, la filosofía de las secuenciales es la misma, se adiciona por el fin (base, cola), se elimina por la cabeza. Sin embargo, existe algún cambio de Figura 2.9. Ejemplo de una Cola encadenada. nomenclatura. Las estructuras de listas se emplean mucho en los sistemas de comunicación a partir de los computadores. Se involucran en la cola los procesos o mensajes y se van atendiendo a medida como fueron llegando. Figura 2.10. Agregar un nodo en una Cola encadenada. Algoritmos de agregar y eliminar nodos en una cola encadenada ADDCOLAE(cola,cab,dato) info.nodo <- dato link.nodo <- null SI cola = null TH cola,cab <- nodo SN link.cola <-nodo cola <- nodo FSI FINADDCOLAE() Similarmente se plantea el algoritmo de eliminación de nodos en una cola encadenada. Facultad de Ingeniería SUBCOLAE(cola,cab,dato) SI cola = null TH "cola no existe" SN info.cab -> dato cab <- link.cab SI cab = null TH cola = null FSI FSI FINSUBCOLAE() 30 Figura 2.11. Eliminar un nodo de una Cola encadenada. La cola secuencial puede considerarse una área de memoria circular, en la cual cuando los N nodos se llenan, es posible volver a iniciar en la cabeza (??). Ejemplo. Se tiene una cadena circular que contiene la información de exponentes y coeficientes de un polinomio en desorden, se requiere ordenarla. NOTA. El nodo guía tiene coeficiente y exponente 0 (cero). Podría existir dos nodos con el mismo exponente debido al desorden. Todos los exponentes son positivos. No existen nodos con coeficientes cero. INICIO() // desarrollo utilizando la misma cadena para ello SI POL = null TH ESCRIBA "la cadena no existe" SN SI POL = link.POL TH ESCRIBA "la cadena esta vacia" SN SI exp.POL ><0 TH ESCRIBA "el nodo guía no es el adecuado" s <-- 1 MQ s >< 0 p <-- POL q <-- link.p r <-- link.q MQ exp.q < exp.r p <-- link.p q <-- link.p r <-- link.q SI R = POL TH s <-- 0 FSI FMQ SI s = 1 TH SI exp.q = exp.r TH coef.q = coef.q+coef.r SI coef.q = 0 TH link.p <-- link.r SN link.q <-- link.r FSI FSI SI coeg.q > coef.r TH aux <-- link.r link.p <-- r link.r <-- q link.q <-- aux FSI Luis Carlos Torres Soler Estructuras de Datos s <-- 1 FSI FMQ ESCRIBA " polinomio ordenado " FSI FSI FSI FIN() Ejemplo. El ejemplo anterior empleando otra cadena ALG() // ordenarla utilizando otra cadena. SI POL = null TH ESCRIBA "la cadena no existe" SN SI POL = link.POL TH ESCRIBA "la cadena esta vacia" SN SI exp.POL ><0 TH ESCRIBA "el nodo guía no es el adecuado" SN p <-- POL q <-- link.p BUSCAR_NODO() exp.nodo <-- 0 coef.nodo <-- 0 q <-- dir.nodo link.q <-- q s <-- 0 MQ q >< POL p1 <-- q q1 <-- link.p1 SI s = 0 TH BUSCAR_NODO() FSI exp.nodo <-- exp.q coef.nodo <-- coef.q s <-- 1 SI q1 = q TH link.p1 <-- dir.nodo link.nodo <-- q1 s <-- 0 VAYA siga FSI MQ exp.q1 < exp.q p1 <-- q1 q1 <-- link.p1 SI q1 = q TH link.p1 <-- dir.nodo link.nodo <-- q1 s <-- 0 VAYA siga FSI FMQ SI exp.q1 = exp.q TH coef.q1 <-- coef.q1 + coef.q SN link.p1 <-- dir.nodo link.nodo <-- q1 FSI siga: p <-- link.p q <-- link.p FMQ ESCRIBA " polinomio ordenado " Facultad de Ingeniería 31 FSI 32 FSI FSI FIN() Ejemplo. Insertar-borrar en una cola encadenada con un Stack de disponibles. Alg() Principal() "teclee opción" SI opción = I TH INSERTAR() SN SI opción = B TH BORRAR() SN "teclee opción correcta" FSI FSI Fin_Principal() INSERTAR() SI Dispo = null TH ERROR Overflow SN nodo <- Dispo Dispo <- link.dispo Info.nodo <- INFOR link.nodo <- nodo col <- nodo FSI FININSERTAR() BORRAR() SI col = null TH ERROR underflow SN info.col -> INFOR Q <- cab cab <- link.cab link.Q <- Dispo Dispo <- Q FSI FINBORRAR() 2.4. Doble Cola (bicola) Una doble cola es un conjunto de nodos en la cual la inserción o borrado puede realizarse por la cabeza o por la cola. Igualmente, hay doble cola secuencial y encadenada. Las operaciones que se pueden realizar en la doblecola son: a. Agregar un nodo. b. Eliminar un nodo. Algoritmo de agregar un nodo en la cabeza. Luis Carlos Torres Soler Estructuras de Datos 33 Figura 2.12. Dobles colas: secuencial y encadenada. ADDDCOLAS(cola,cab,dato) SI cola = null TH cola=cab info.cola <- dato SN SI (cab-cola < N) TH cab <- cab - C info.cab <- dato SN "la doblecola esta llena" FSI FSI FINADDDCOLAS() Algoritmo de agregar un nodo en la cola. ADDDCOLAS(cola,cab,dato) SI cola = null TH cola=cab info.cola <- dato SN SI (cab-cola < N) TH cola <- cola + C info.cola <- dato SN "la doblecola esta llena" FSI FSI FINADDDCOLAS() 2.5. Aplicaciones de Pilas Todas las instrucciones de un lenguaje tienen dos cosas en común: operadores y operandos. Un operando es una constante, un identificador (variable), o un label que se usa para realizar una operación de bifurcación o identificación, un operador representa la acción u operación que se va a realizar. 2 La gran mayoría de los lenguajes representan las expresiones aritméticas en la llamada forma INFIJA, en la cual los operadores se encuentran entre los operandos. El proceso de compilar un programa escrito en algún lenguaje de alto nivel requiere de guardar adecuadamente los operandos y operadores para facilitar la evaluación de las operaciones, para ello, los compiladores emplean la notación POSFIJA (o POLACA). En esta notación los operadores aparecen después de los operandos. Ejercicio. Conteste en forma concreta lo siguiente: 2 Se ha dado un conjunto de ejemplos y ejercicios donde se muestran diferentes aplicaciones, pero aquí se enuncian aplicaciones particulares. Facultad de Ingeniería a. ?Cuáles son dos diferencias y dos semejanzas entre una lista y una cola?. 34 b. ?Por qué en un Stack sólo puede trabajarse desde la cabeza? c. ?Cuál es la razón para que la lectura en posorden de un árbol que representa una expresión de asignación aritmética sea igual a la notación posfija de la misma expresión? 2.5.1. Notación Posfija La notación posfija se usa para representar expresiones empleadas en un lenguaje de una forma que especifica el orden de cálculo según los operadores. 1. Los operandos en la notación posfija van en el mismo orden de la notación infijo. 2. Los operadores en la notación posfija aparecen en el orden que deben calcularse. 3. Los operadores aparecen a continuación de sus operandos. i) <operando> --> <identificador> | <operando><operando><operador> ii) <operador> --> + | - | * | / | ^ El signo menos unario (y otros operadores unarios) se pueden representar por otro símbolo. Ejemplo. @, %, &, etc., y se emplea: iii) <operando> --> <operando>( @ | & | % ) Notación posfija en expresiones aritméticas. Se utiliza una pila FIFO (cola) para la notación infija, otra para la notación posfija y un stack (pila LIFO) para los operadores. Las siguientes reglas se utilizan para pasar una expresión aritmética en infijo a una expresión en notación posfija. 1. Si el elemento que se lee de la expresión infija es una variable o constante (operando), se pasa directamente a la pila que contiene la notación posfija. 2. Si el elemento que se lee de la expresión infija es un operador, se pasa al stack de operadores desplazando antes de él para la pila de notación posfija todos los operadores de mayor o igual jerarquía (prioridad). NOTA. Se considera en todo lenguaje la siguiente tabla de prioridades: Luis Carlos Torres Soler ( paréntesis -1 = asignación 0 +, - suma, resta 1 35 Estructuras de Datos *, / multiplicación, división 2 ^ potenciación 3 3. Si el elemento que se lee es un paréntesis abierto "(", pasa directamente al stack de operadores. 4. Si el elemento que se lee es un paréntesis cerrado ")", todos los operadores que están en el stack hasta hallar un paréntesis abierto "(" pasan a la pila de la notación posfija, eliminando el paréntesis "(". 5. Si se ha terminado de leer la expresión los operadores que están en el stack pasan a la pila de la notación posfija. NOTA: no puede aparecer en la notación posfijo ningún paréntesis abierto. Ejemplo. Sea la expresión X=(Y-(U+V/H)*S)+Y^A. LEE Facultad de Ingeniería Pila de Notación Posfijo Stack - operadores X X = X = ( X =( Y XY =( - XY =(- ( XY =(-( U XYU =(-( + XYU =(-(+ V XYUV =(-(+ / XYUV =(-(+/ H XYUVH =(-(+/ ) XYUVH/+ =(- * XYUVH/+ =(-* S XYUVH/+S =(-* ) XYUVH/+S*- = + XYUVH/+S*- =+ 36 LEE Pila de Notación Posfijo Stack - operadores Y XYUVH/+S*-Y =+ ^ XYUVH/+S*-Y =+^ A XYUVH/+S*-YA =+^ @ XYUVH/+S*-YA^+= 2.5.2. Expresiones aritméticas en notación posfija Igualmente se cumplen normas para la evaluación o cálculo de las expresiones una vez que se hallan en notación posfija, se utiliza para ello un stack. 1. Si el símbolo explorado es un operando (identificador o constante) ponerlo en la pila y explorar el símbolo siguiente. 2. Si el símbolo explorado es un operador unario, aplicarlo al operando superior de la pila y sustituirlo por el resultado. 3. Si el símbolo explorado es un operador binario, aplicarlo a los dos operandos superiores de la pila y sustituirlos por el resultado. Para la expresión X=(Y-(U+V/H)*S)+Y^A el proceso de evaluación para la notación posfija sería: Lee Stack de evaluación X Y U V H / + S * Y A ^ + = X XY XYU XYUV XYUVH X Y U (V/H) X Y (U+(V/H)) X Y (U+(V/H)) S X Y ((U+(V/H))*S) X (Y-((U+(V/H))*S)) X (Y-((U+(V/H))*S)) Y X (Y-((U+(V/H))*S)) Y A X (Y-((U+(V/H))*S)) (Y^A) X (Y-((U+(V/H))*S))+(Y^A) (X=(Y-((U+(V/H))*S))+(Y^A)) Luis Carlos Torres Soler Cada paréntesis indica una celda del stack