5
Escuela Superior de Inform´ atica Aplicada Resoluci´ on del Examen Final de Septiembre de 2004 de Estructuras de Datos y Algoritmos 1. (1.5 puntos) La siguiente clase Java representa un Grafo etiquetado y ponderado mediante Listas de Adyacencia: public class Grafo { protected Vertice vectorVertices[]; protected int numVertices; protected static final int TAMANYO_POR_DEFECTO = ...; protected Diccionario diccionarioVertices; public Grafo(){...} public void insertarArista(String nomVOrigen, String nomVDestino, int coste){...} public void insertarVertice(String nomVertice){...} public String toString(){...} } Se dispone tambi´ en de las clases Arista, Vertice y, como se utiliza en Vertice, ListaConPI: class Arista { int verticeDestino; int coste; Arista(int verticeDestino, int coste){...} } class Vertice { String nombre; int codigo; ListaConPI listaAdyacencia ; Vertice(String nombreVertice){...} Vertice(String nombreVertice, int codigoVertice){...} } public interfaz ListaConPI { void insertar(Object x); // inserta x antes del que ocupa el Punto de Inter´ es, que queda inalterado void borrar() throws ElementoNoEncontrado; // borra el elemento que ocupa el Punto de Inter´ es, que // apuntar´ a al elemento que segu´ ıa al que se ha borrado void inicio(); // sit´ ua el Punto de Inter´ es al principio de la Lista void fin(); // sit´ ua el Punto de Inter´ es al final de la Lista, detr´ as del ´ ultimo elemento void siguiente() throws ElementoNoEncontrado; // avanza el Punto de Inter´ es Object recuperar() throws ElementoNoEncontrado; // recupera el elemento que ocupa el Punto de Inter´ es boolean esFin(); // true si el Punto de Inter´ es est´ a en fin() de Lista, detr´ as del ´ ultimo elemento boolean esVacia(); // devuelve true si la lista no tienen ning´ un elemento y false en caso contrario } A partir de las definiciones anteriores, se desea a˜ nadir a la clase Grafo un nuevo m´ etodo, estaVertice, que dado un Grafo obtenga el nombre del primer V´ ertice sobre el que incidan todos los del Grafo, ´ el mismo incluido; si no existe ning´ un V´ ertice con esta caracter´ ıstica se lanzar´a la excepci´on ElementoNoEncontrado. Se pide dise˜ nar el m´ etodo estaVertice de forma que obtenga su resultado visitando cada Arista del Grafo una s´ ola vez. El dise˜ no del m´ etodo se estructura en dos pasos: PASO 1: para calcular el grado de entrada de los numVertices del Grafo, recorrer cada Lista de Adyacencia de vectorVertices: si el ertice j, j = 0..numVertices-1, est´a en la ListaConPI vectorVertices[i].listaAdayacencia i = 0..numVertices-1 , entonces i incide en j por lo que el grado de Entrada actual de j se incrementa en uno. Para codificar el resultado de este recorrido se utiliza un vector auxiliar de numVertices Enteros, int gradoEntradaVertice[], tal que gradoEntradaVertice[i] representa el grado de Entrada del V´ ertice i; PASO 2: usqueda del primer elemento de gradoEntradaVertice cuyo valor sea numVertices, lo que indica que sobre ´ el inciden todos los del Grafo. Si est´ a, sup´ ongase que es el V´ ertice h, entonces vectorVertices[h].nombre es el resultado del m´ etodo; sino se lanza la Excepcion ElementoNoEncontrado. 1

Septiembre 2004 f Sol

Embed Size (px)

DESCRIPTION

Examen de septiembre de 2004 de estructuras de datos y algoritmos

Citation preview

  • Escuela Superior de Informatica AplicadaResolucion del Examen Final de Septiembre de 2004 de Estructuras de Datos y Algoritmos

    1. (1.5 puntos) La siguiente clase Java representa un Grafo etiquetado y ponderado mediante Listas de Adyacencia:

    public class Grafo {protected Vertice vectorVertices[];protected int numVertices;protected static final int TAMANYO_POR_DEFECTO = ...;protected Diccionario diccionarioVertices;public Grafo(){...}public void insertarArista(String nomVOrigen, String nomVDestino, int coste){...}public void insertarVertice(String nomVertice){...}public String toString(){...}

    }

    Se dispone tambien de las clases Arista, Vertice y, como se utiliza en Vertice, ListaConPI:

    class Arista {int verticeDestino;int coste;Arista(int verticeDestino, int coste){...}

    }class Vertice {

    String nombre;int codigo;ListaConPI listaAdyacencia ;Vertice(String nombreVertice){...}Vertice(String nombreVertice, int codigoVertice){...}

    }public interfaz ListaConPI {

    void insertar(Object x); // inserta x antes del que ocupa el Punto de Interes, que queda inalteradovoid borrar() throws ElementoNoEncontrado; // borra el elemento que ocupa el Punto de Interes, que

    // apuntara al elemento que segua al que se ha borradovoid inicio(); // situa el Punto de Interes al principio de la Listavoid fin(); // situa el Punto de Interes al final de la Lista, detras del ultimo elementovoid siguiente() throws ElementoNoEncontrado; // avanza el Punto de InteresObject recuperar() throws ElementoNoEncontrado; // recupera el elemento que ocupa el Punto de Interesboolean esFin(); // true si el Punto de Interes esta en fin() de Lista, detras del ultimo elementoboolean esVacia(); // devuelve true si la lista no tienen ningun elemento y false en caso contrario

    }

    A partir de las deniciones anteriores, se desea anadir a la clase Grafo un nuevo metodo, estaVertice, que dado un Grafoobtenga el nombre del primer Vertice sobre el que incidan todos los del Grafo, el mismo incluido; si no existe ningun Verticecon esta caracterstica se lanzara la excepcion ElementoNoEncontrado.

    Se pide disenar el metodo estaVertice de forma que obtenga su resultado visitando cada Arista del Grafo una sola vez.

    El diseno del metodo se estructura en dos pasos:

    PASO 1: para calcular el grado de entrada de los numVertices del Grafo, recorrer cada Listade Adyacencia de vectorVertices: si el Vertice j, j = 0..numVertices-1, esta en la ListaConPIvectorVertices[i].listaAdayacencia i = 0..numVertices-1 , entonces i incide en j por lo que el gradode Entrada actual de j se incrementa en uno. Para codicar el resultado de este recorrido se utiliza un vectorauxiliar de numVertices Enteros, int gradoEntradaVertice[], tal que gradoEntradaVertice[i] representael grado de Entrada del Vertice i;

    PASO 2: busqueda del primer elemento de gradoEntradaVertice cuyo valor sea numVertices, lo que indica quesobre el inciden todos los del Grafo. Si esta, supongase que es el Vertice h, entonces vectorVertices[h].nombrees el resultado del metodo; sino se lanza la Excepcion ElementoNoEncontrado.

    1

  • public String estaVertice() throws ElementoNoEncontrado{String res = "";int gradoEntradaVertice[] = new int[numVertices];for ( int i = 0; i < numVertices; i++) gradoEntradaVertice[i] = 0;//Paso 1: calculo del grado de entrada de cada Verticefor (int i = 0; i < numVertices; i++) {

    // actualizar el grado de entrada de los Vertices adyadcentes al iListaConPI adyAlI = vectorVertices[i].listaAdyacencia;adyAlI.inicio();try{

    while ( !adyAlI.esFin()) {Arista actual = (Arista)adyAlI.recuperar();int numActual = actual.verticeDestino;gradoEntradaVertice[numActual]++;adyAlI.siguiente();

    }} catch(ElementoNoEncontrado e){;}

    }//Paso 2: busqueda del Vertice con grado de entrada numVerticesint i = 0;while (i < numVertices && gradoEntradaVertice[i] != numVertices ) i++;if ( i == numVertices )

    throw new ElementoNoEncontrado("No hay ningun Vertice sobre el que inciden todos");else res = vectorVertices[i].nombre;return res;

    }

    2. (2.75 puntos) Sea el interfaz ListaConPI descrito en la pregunta anterior; a continuacion gura la clase ListaConPIEnlazadaque lo implementa mediante una Lista Enlazada de NodoLista con tres referencias: una a su primer Nodo, que es un NodoFicticio o Cabecera, una a su ultimo Nodo y, nalmente, una al Nodo anterior al que ocupa el Punto de Interes.

    public class ListaConPIEnlazada implements ListaConPI {protected NodoLista pri, ant, ult;public ListaConPIEnlazada() {

    pri = ult = ant = new NodoLista( null );}public void insertar(Object x) {

    NodoLista nuevo = new NodoLista(x);nuevo.siguiente = ant.siguiente;ant.siguiente = nuevo;if ( ant == ult) ult = ant.siguiente;ant = ant.siguiente;

    }void borrar() throws ElementoNoEncontrado {...};public void inicio(){...};public void fin(){...};public void siguiente() throws ElementoNoEncontrado{...};public Object recuperar() throws ElementoNoEncontrado{...};public boolean esFin(){...};public boolean esVacia(){...};public String toString (){...};

    }

    Se pide:

    a) utilizando unica y exclusivamente los metodos del interfaz ListaConPI y con coste lineal, disenar un metodo borrarTodosque borre todas las apariciones de un cierto x en una ListaConPI dada; si x no fuera un elemento de la ListaConPI selanza la Excepcion ElementoNoEncontrado (1.25 puntos);

    b) discutir en terminos de coste por que no tiene sentido plantear una implementacion del interfaz ListaConPI sobre unarepresentacion de array (0.5 puntos);

    c) re-escribir el metodo insertar de la clase ListaConPIEnlazada cuando en lugar del atributo ant se dene otro, NodoListaactual, que referencia al Nodo que ocupa el Punto de Interes actual, y no al anterior como haca ant (1 punto).

    2

  • Utilizando solo los metodos del interfaz ListaConPI, disenar de borrarTodos con coste lineal:

    public void borrarTodos(Object x) throws ElementoNoEncontrado {boolean estaX = false;inicio();// recorrido de this ListaConPI para borrar todas las apariciones de xwhile ( !esFin() ){

    Object actual = recuperar();if ( actual.equals(x) ) {

    borrar();estaX = true;

    }// solo se avanza el Punto de Interes cuando NO se borraelse siguiente();

    }if ( !esta ) throw new ElementoNoEncontrado();

    }

    Tiene sentido plantear una implementacion del interfaz ListaConPI sobre una representacion de array:no, por dos motivos relacionados con la semantica del Punto de Interes:

    insertar un nuevo elemento manteniendo el Punto de Interes, salvo en fin() de ListaConPI, supone desplazaruna posicion a la derecha todos los elementos ocupados del array a partir de este;

    borrar el elemento que ocupa el Punto de Interes supone desplazar una posicion a la izquierda todos los elementosocupados del array posteriores a este.

    Metodo insertar de ListaConPIEnlazada cuando en lugar de ant se dispone de una referencia al Puntode Interes actual, actual:

    public void insertar(Object x) {NodoLista nuevo = new NodoLista(x);// Recorrido de la ListaConPI hasta el Nodo anterior al apuntado por actualNodoLista pAnt = pri;while ( pAnt.siguiente != actual ) pAnt = pAnt.siguiente;nuevo.siguiente = actual;pAnt.siguiente = nuevo;if ( pAnt == ult ) ult = nuevo;

    }

    3. (3.5 puntos) Sea un array v de v.length Objetos distintos y ordenados ascendentemente. Para construir ecientemente unArbol Binario de Busqueda equilibrado con los mismos elementos que v, se decide anadir el siguiente constructor a la claseArbolBinarioBusqueda:

    public ArbolBinarioBusqueda(Object v[]){ raiz = NodoBinario.seConstruye(v,0,v.length-1);}donde seConstruye es un metodo recursivo Divide y Venceras (DyV) de la clase NodoBinario:

    final class NodoBinario {Object dato; NodoBinario izq, der;NodoBinario(){ this(null, null, null); }NodoBinario(Object x) { this(x, null, null);}NodoBinario(Object x, NodoBinario izquierdo, NodoBinario derecho) {

    dato = x; izq = izquierdo; der = derecho;} ...

    }

    Se pide:

    a) dado un ABB vaco, insertarle en secuencia los N elementos de v origina un ABB completamente degenerado con costedel orden de N*log N. A partir de esta armacion, explicar como es posible construir un ABB equilibrado a partir delarray v si se aplica la estrategia DyV y en que consisten sus etapas DIVIDIR, VENCER y COMBINAR (1 punto);

    b) escribir el metodo Java seConstruye que resulta de la respuesta dada en el apartados anterior (1.5 puntos);

    c) escribir la talla, casos, relaciones de recurrencia y complejidad del metodo seConstruye disenado (1 punto).

    3

  • Por que y como se aplica la estrategia DyV al problema enunciado:Sea N = v.length el numero de elementos de v. Al estar ordenados y ser distintos entre s, su elemento central, X =v[(N-1)/2], es mayor que los (N-1)/2 elementos del subvector vIzq = v[0..(N-3)/2] y menor que los (N-1)/2 delsubvector vDer = v[(N+1)/2..N-1]. Esta misma propiedad se puede establecer para los elementos centrales de lossubvectores vIzq y vDer y, as, recursivamente, para cualquier elemento central de cualquier subvector de v de tallamayor o igual que uno.Por tanto, como cualquier NodoBinario de un ABB satisface la propiedad de busqueda ordenada, i.e. su dato X essiempre mayor que los datos de su NodoBinario izq y menor que los datos de su NodoBinario der, a partir de vsolo se contruye un NodoBinario equilibrado sii para cada subvector de talla mayor o igual que uno:

    su dato es el elemento central del subvector de v correspondiente;

    su NodoBinario izq se construye a partir del subvector vIzq de v correspondiente;

    su NodoBinario der se construye a partir del subvector vDer de v correspondiente;

    Notese que el paso DIVIDIR corresponde al calculo de la posicion central de cada subvector v[izq,..,der], quepermite dividirlo en tres partes: su elemento central, vIzq y vDer; el paso VENCER consiste en resolver para vIzq yvDer el mismo problema planteado para v[izq,..,der], se construye; nalmente, el paso COMBINAR correspondea las tres asignaciones que construyen el NodoBinario equilibrado con los mismos elementos que v[izq,..,der]: datodel NodoBinario resultado es el elemento central del subvector v[izq,..,der], su hijo izquierdo es el NodoBinarioresultante de se construye a partir de vIzq y su hijo derecho es el NodoBinario resultante de se construye a partirde vDer.

    Metodo Java seConstruye:

    static NodoBinario seConstruye(Object v[], int izq, int der){NodoBinario nuevo = null;if ( izq

  • Suponiendo que buscarKesimo se aplica a un ABB equilibrado, se pide:

    a) expresar la talla, casos, relaciones de recurrencia y complejidad del metodo buscarKesimo; se recuerda ahora que elmetodo tamanyo tiene siempre un coste lineal (1 punto);

    b) en ningun caso el coste de buscarKesimo es logartmico por que motivo? (0.5 puntos);

    c) indicar que condicion se tendra que cumplir para que el coste en el peor de los casos fuera logartmico y explicarque modicaciones se tendran que realizar en las clases NodoBinario y ArbolBinarioBusqueda (0.75 puntos);

    Talla, casos, relaciones de recurrencia y complejidad de buscarKesimo de un ABB equilibrado: si el ABBesta equilibrado, la talla del problema es su tamano, N; si el ABB no estuviera equilibrado entonces la talla dependeraademas de k.De nuevo, sii el ABB esta equilibrado no existen instancias signicativas de buscarKesimo: calcular siempre el tamanode su sub-Arbol izquierdo, int tama~noIzq = NodoBinario.tamanyo(r.izq), supone un coste lineal con la talla der.izq, que a la postre resulta siempre lineal con N. Por ejemplo,

    si se busca el elemento mediana del ABB, que es su raz, tbuscarKesimo(N) = c*N/2 + c, donde N/2 es elcoste de calcular el tamano del sub-Arbol izquierdo de un ABB equilibrado.

    si k no es una posicion legal del ABB equilibrado o k es la posicion de una de las hojas del ABB, aunque seproduce el maximo numero de llamadas recursivas, en cualquiera de ellas se calcula el tamano del Nodo r.izq,que si r esta equilibrado es siempre la mitad del tamano de r. Por tanto, el coste del metodo buscarKesimoseguira resultado lineal con N; veamoslo mediante las relaciones de recurrencia:

    tbuscarKesimo(n >= 1) = 1 * tbuscarKesimo(n/2) + c * n/2 tbuscarKesimo(n = 0) = c

    Como en la llamada mas alta n = N, resolviendo por sustitucion se obtendra una cota lineal con N.

    Por tanto, en cualquier caso tbuscarKesimo(N) (N).

    Por que motivo en ningun caso el coste del metodo buscarKesimo es logartmico:De la discusion anterior queda claro que el motivo es que el coste del metodo NodoBinario.tamanyo es lineal con latalla del problema.

    Condicion para que en el peor de los casos el coste de buscarKesimo sea logartmico. Modificaciones arealizar en NodoBinario y ArbolBinarioBusquedaSi el coste del metodo NodoBinario.tamanyo es independiente de la talla del problema, del orden de una constante,buscarKesimo s tiene instancias:

    Mejor Caso: si se busca el elemento mediana del ABB, tbuscarKesimo(N)m = c + c (1), donde c es el

    nuevo coste de calcular el tamano del sub-Arbol izquierdo de un ABB equilibrado.

    Peor Caso: si k no es una posicion legal del ABB equilibrado o k es la posicion de una de las hojas del ABB, lasnuevas relaciones de recurrencia que expresan el coste seran:

    tbuscarKesimo(n >= 1)p = 1 * tbuscarKesimo(n/2)p + c tbuscarKesimo(n = 0) = c

    Si n = N, resolviendo por sustitucion se obtendra una cota logartmica.

    Ahora bien, para conseguir que el coste del metodo tamanyo sea del orden de una constante sera necesario realizar lassiguientes modicaciones:

    en la clase NodoBinario,* anadir un nuevo atributo, int tamanyo para representar el tamano de un NodoBinario;* anadir en su constructora con tres parametros una nueva instruccion, tamanyo = 1; para inicializar el

    tamano de un NodoBinario en el momento de su construccion;

    en la clase ArbolBinarioBusqueda,* el metodo tamanyo se dene ahora como una consulta al atributo tamanyo de la clase NodoBinario;* el metodo insertar actualiza, incrementa en 1, el tamano de los Nodos del camino de insercion;* el borrar actualiza, decrementa en 1, el tamano de los Nodos del camino de borrado.

    5