La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

clase 23: Stacks y Queues Problema.

Presentaciones similares


Presentación del tema: "clase 23: Stacks y Queues Problema."— Transcripción de la presentación:

1 clase 23: Stacks y Queues Problema. Leer las líneas de un archivo que contiene un programa en Java y verificar que todos los paréntesis estén bien balanceados Use el TDA (Tipo de Dato Abstracto) Stack (Pila) Stack: contenedor que opera con el principio LIFO (Last In First Out), es decir, el último elemento que ingresa es el primero que sale. TDA: tipo de dato con implementación oculta (representación privada y operaciones públicas) 27-octubre J.Alvarez

2

3 static public void main(String[]args) {
Stack s=new Stack(); BR A=new BR(new FR(args[0])); String linea; boolean ok = true; while((linea=A.readLine())!=null){ for(int i=0; i< linea.size(); i++) { char c = linea.charAt(i); if (c == ´{´ || c== ´(´ || == ´[´) { if(s.full()) U.abort(“overflow”); if (c == ´{´) s.push(“}”); else if (c == ´[´) s.push(“]”); else s.push(“)”); } else if (c == ´}´ || c== ´)´ || == ´]´) { if(s.empty() || !c.equals((String)s.pop()) { ok = false; break; } } //stack deberia estar vacío al final if ( !s.empty() || !ok) System.out.println(“error en los paréntesis”);

4

5 clase 23: Stacks y Queues 27-octubre J.Alvarez

6

7

8 Solución 2: atrapando las excepciones (“el golpe avisa”)
static public void main(String[]args) throws Exception{ Stack s=new Stack(); BR A=new BR(new FR(args[0])); String linea; boolean ok = true; try { while((linea=A.readLine())!=null){ for(int i=0; i< linea.size(); i++) { char c = linea.charAt(i); if (c == ´{´ || c== ´(´ || == ´[´) { s.push(c+””); //ponemos un String (objeto) } else if (c == ´}´ || c== ´)´ || == ´]´) { if(!c.equals((String)s.pop()) { ok = false; break; } } } catch (StackEmpty e) { ok = false; } } catch (StackFull e) { U.abort(“overflow”); } //stack deberia estar vacío al final if (!ok) System.out.println(“error en los paréntesis”);

9

10

11

12

13

14

15 Alternativamente: public void push(Object x) throws StackFull {
//agregar al comienzo primero=new Nodo(x, primero); //1º| x | || y | | … }

16 public Object pop() throws StackEmpty
{ //excepción si lista está vacía if(primero==null) throw new StackEmpty(); //recuperar primer valor Object aux=primero.valor;//1º|x|||y|| //eliminar primer nodo primero=primero.sgte; ;//1º|y||… //devolver primer valor return aux; } }//fin class Stack

17

18 Ejemplo de uso: eliminación de recursividad
Suponga que una función F realiza un llamado recursivo dentro de su código, lo que se ilustra en la siguiente figura:

19 Tail Recursion Si la llamada recursiva es lo último que hace la función F, se puede substituir por un ciclo while. Este caso es conocido como tail recursion y es muy simple eliminarla. Ejemplo: void imprimir(int[] a, int j) // versión recursiva{ if (j<a.length) { System.out.println(a[j]); imprimir(a, j+1); // tail recursion } void imprimir(int[] a, int j) // versión iterativa{ while (j<a.length) { j=j+1;

20 Caso General: uso de pilas
Por ejemplo: recorrido en preorden de un arbol binario. // "raiz" es la referencia a la raiz del arbol // llamado inicial: preorden(raiz)  // version recursiva  void preorden(Nodo nodo){ if (nodo!=null) { System.out.print(nodo.elemento); preorden(nodo.izq); preorden(nodo.der); }

21 Preorden no recursivo: primera versión
void preorden(Nodo nodo){ Nodo aux; Pila pila=new Pila(); // pila de nodos pila.apilar(nodo); while(!pila.estaVacia()) // mientras la pila no este vacia { aux=pila.desapilar(); if (aux!=null) { System.out.print(aux.elemento); // primero se apila el nodo derecho y luego el izquierdo // para mantener el orden correcto del recorrido // al desapilar los nodos pila.apilar(aux.der); pila.apilar(aux.izq); }

22 Preorden no recursivo: segunda versión
dado que siempre el ultimo nodo apilado dentro del bloque if es aux.izq podemos asignarlo directamente a aux hasta que éste sea null, es decir, el bloque if se convierte en un bloque while y se cambia el segundo apilar por una asignación de la referencia  void preorden(Nodo nodo){ Nodo aux; Pila pila=new Pila(); // pila de nodos pila.apilar(nodo); while(!pila.estaVacia()) // mientras la pila no este vacia { aux=pila.desapilar(); while (aux!=null) { System.out.print(aux.elemento); pila.apilar(aux.der); aux=aux.izq; }

23 Corolario Si bien los programas no recursivos son más eficientes que los recursivos, la eliminación de recursividad (excepto en el caso de tail recursion) le quita claridad al código del programa. Por lo tanto: A menudo es conveniente eliminar el tail recursion. Un método recursivo es menos eficiente que uno no recursivo, pero sólo en pocas oportunidades vale la pena eliminar la recursión.

24

25

26

27

28

29

30

31 clase 23: Stacks y Queues 27-octubre J.Alvarez

32

33

34 Clase25: Listas Enlazadas

35

36 TDA Cola de prioridad Una cola de prioridad es un tipo de datos abstracto que almacena un conjunto de datos que poseen una llave perteneciente a algún conjunto ordenado, y permite insertar nuevos elementos y extraer el máximo (o el mínimo, en caso de que la estructura se organice con un criterio de orden inverso). Es frecuente interpretar los valores de las llaves como prioridades, con lo cual la estructura permite insertar elementos de prioridad cualquiera, y extraer el de mejor prioridad. Dos formas simples de implementar colas de prioridad son: Una lista ordenada: Inserción: O(n) Extracción de máximo: O(1) Una lista desordenada: Inserción: O(1) Extracción de máximo: O(n)

37 Heaps Un heap es un árbol binario de una forma especial, que permite su almacenamiento en un arreglo sin usar punteros. Un heap tiene todos sus niveles llenos, excepto posiblemente el de más abajo, y en este último los nodos están lo más a la izquierda posible. Ejemplo: La numeración por niveles (indicada bajo cada nodo) son los subíndices en donde cada elemento sería almacenado en el arreglo. En el caso del ejemplo, el arreglo sería:

38 Implementación de Heaps
La característica que permite que un heap se pueda almacenar sin punteros es que, si se utiliza la numeración por niveles indicada, entonces la relación entre padres e hijos es: Hijos del nodo j = {2*j, 2*j+1} Padre del nodo k = floor(k/2) Un heap puede utilizarse para implementar una cola de prioridad almacenando los datos de modo que las llaves estén siempre ordenadas de arriba a abajo (a diferencia de un árbol de búsqueda binaria, que ordena sus llaves de izquierda a derecha). En otras palabras, el padre debe tener siempre mayor prioridad que sus hijos .

39 Inserción en Heaps Agregar el nuevo elemento en la primera posición libre del heap, esto es, el próximo nodo que debería aparecer en el recorrido por niveles -> al final del arreglo. Con esto la forma del heap se preserva, pero la restricción de orden no tiene por qué cumplirse. Solución: hacerlo “subir” hasta un punto donde se vuelva a cumplir la condición a[++n]=x; for(j=n; j>1 && a[j]>a[j/2]; j/=2){ # intercambiamos con el padre t=a[j]; a[j]=a[j/2]; a[j/2]=t; } El proceso de inserción, en el peor caso, toma un tiempo proporcional a la altura del árbol, esto es, O(log n).

40 Extracción del máximo (eliminación)
El máximo está en la raíz del árbol (casillero 1 del arreglo). Al sacarlo de ahí, ese lugar queda vacante. Para llenarlo, tomamos al último elemento del heap y lo trasladamos al lugar vacante. En caso de que no esté bien ahí de acuerdo a su prioridad (¡que es lo más probable!), lo hacemos descender intercambiándolo siempre con el mayor de sus hijos. Es decir "se hunde" hasta su nivel de prioridad. m=a[1]; # La variable m lleva el máximo a[1]=a[n--]; # Movemos el último a la raíz y achicamos el heap j=1; while(2*j<n) # mientras tenga algún hijo { k=2*j; # el hijo izquierdo if(k+1<=n && a[k+1]>a[k]) k=k+1; # el hijo derecho es el mayor if(a[j]>a[k]) break; # es mayor que ambos hijos t=a[j]; a[j]=a[k]; a[k]=t; j=k; # lo intercambiamos con el mayor hijo } Este algoritmo también demora un tiempo proporcional a la altura del árbol en el peor caso, esto es, O(log n).

41 Transformar un arreglo en un heap
Sacando lso elemento del arreglo y poniéndolos en un heap: O(n log n) y ocupa el doble de memoria Modificarlo para hacerlo más eficiente y en el mismo arreglo. La idea es invertir el orden de las "mitades" del arreglo, haciendo que el "input" esté a la izquierda y el "heap" a la derecha.

42 Transformar un arreglo en un heap
En realidad, si el "heap" está a la derecha, entonces no es realmente un heap, porque no es un árbol completo (le falta la parte superior), pero sólo nos interesa que en ese sector del arreglo se cumplan las relaciones de orden entre a[k] y {a[2*k],a[2*k+1]}. En cada iteración, se toma el último elemento del "input" y se le "hunde" dentro del heap de acuerdo a su nivel de prioridad. La razón es que, al ser "hundido", un elemento paga un costo proporcional a su distancia al fondo del árbol. Dada las características de un árbol, la gran mayoría de los elementos están al fondo o muy cerca de él, por lo cual pagan un costo muy bajo. En un análisis aproximado, la mitad de los elementos pagan 0 (ya están al fondo), la cuarta parte paga 1, la octava parte paga 2, etc. Sumando todo esto, tenemos que el costo total está acotado por


Descargar ppt "clase 23: Stacks y Queues Problema."

Presentaciones similares


Anuncios Google