📝 Examen Maestro EDA — 22 ejercicios de escribir código Java

1º Grado en Ingeniería Informática · UAX · Convocatoria extraordinaria · Formato del examen real ampliado: 5 bloques · SIN soluciones (están en Examen_Maestro_EDA_SOLUCIONES.html)

Instrucciones. Examen práctico de escribir código Java a mano, con el formato del examen real (5 bloques: abstracción/encapsulación, complejidad, ordenación, estructuras de datos, búsqueda/recursión), pero exhaustivo: cubre todas las variantes que pueden caer. Hazlo en papel y por tandas (un bloque por sesión). Se valora que el código sea sintácticamente correcto, completo y funcional: firma exacta, casos especiales, caso base. En complejidad hay que justificar las cuentas (tamaño del problema + vueltas de cada bucle), no basta el resultado. Las soluciones, con el código compilado, su salida real y las explicaciones, están en Examen_Maestro_EDA_SOLUCIONES.html.
BloqueTemaEjercicios
1Abstracción y encapsulación1.1 – 1.4
2Orden de complejidad (9 fragmentos)2.1 – 2.2
3Métodos de ordenación3.1 – 3.7
4Estructuras de datos4.1 – 4.6
5Búsqueda y recursión5.1 – 5.3
BLOQUE 1 · Abstracción y encapsulación
Diseñar clases con atributos privados, validación, herencia, clases abstractas, interfaces y polimorfismo.
Ejercicio 1.1 · Clase encapsulada con validación: CuentaBancariaencapsulación

Escribe una clase CuentaBancaria correctamente encapsulada:

  1. Atributos privados: titular (String) y saldo (double).
  2. Constructor que reciba ambos y valide: titular no vacío y saldo inicial no negativo (lanza IllegalArgumentException si algo falla).
  3. Getters de ambos. No debe existir setSaldo: razona en una línea por qué es buena idea.
  4. depositar(double): ignora (con aviso por pantalla) cantidades ≤ 0.
  5. retirar(double): devuelve boolean; solo permite retirar si 0 < cantidad ≤ saldo.
  6. Sobrescribe toString().
  7. Un main que cree una cuenta con 100 €, deposite 50, intente retirar 500 (debe fallar), retire 80 y deposite -10, imprimiendo el estado tras cada operación.
Ejercicio 1.2 · Clase abstracta + herencia + polimorfismo: jerarquía de figurasabstracción

Diseña la jerarquía:

  1. Clase abstracta Figura con atributo privado nombre, constructor, getter y métodos abstractos double area() y double perimetro(). Añade un método CONCRETO describir() que imprima nombre, área y perímetro (redondeados a 2 decimales) usando los abstractos.
  2. Subclases Circulo (radio) y Rectangulo (base, altura) que hereden de Figura con extends, llamen a super(...) y sobrescriban los dos métodos con @Override.
  3. Un main que guarde varias figuras en un array de tipo Figura[], las recorra llamando a describir() y acumule la suma de todas las áreas. Explica en 2 líneas qué es el polimorfismo y dónde aparece exactamente en tu main.
  4. Responde: ¿por qué new Figura("x") no compila?
Ejercicio 1.3 · Interfaz + clase abstracta + sobrescritura vs sobrecargaabstracción
  1. Define una interfaz Bonificable con un único método double bonus();
  2. Clase abstracta Empleado: atributos privados nombre y salarioBase, constructor, getters, método abstracto double salarioMensual(), y DOS métodos sobrecargados: subirSalario(double cantidad) y subirSalario(double cantidad, int veces). Sobrescribe toString() para que muestre el nombre y el salario mensual.
  3. EmpleadoFijo (con trienios): extiende Empleado e implementa Bonificable; su salario mensual = base + 50·trienios + bonus() (bonus fijo de 100). EmpleadoTemporal (con horasExtra): salario mensual = base + 15·horasExtra.
  4. Un main con un List<Empleado> que imprima cada empleado y, solo si es instanceof Bonificable, su bonus. Prueba las dos sobrecargas.
  5. Explica con TUS ejemplos la diferencia entre sobrescritura (override) y sobrecarga (overload).
Ejercicio 1.4 · Arreglar una clase mal encapsulada + definir los dos conceptosencapsulación

Dada esta clase:

class ProductoMal {           // ASÍ NO: todo público, sin ningún control
    public String nombre;
    public double precio;
    public int stock;
}
  1. Explica qué problemas tiene tal y como está (pon un ejemplo concreto de estado absurdo que permite).
  2. Reescríbela bien encapsulada: atributos privados, constructor, getters, setters con validación (precio y stock nunca negativos, lanzando IllegalArgumentException) y un método vender(int unidades) que rechace ventas imposibles.
  3. Define en 2-3 líneas cada uno: abstracción y encapsulación, y di cómo se plasma cada una en Java.
BLOQUE 2 · Orden de complejidad
Determina y JUSTIFICA el orden de cada método: di cuál es el tamaño del problema, cuántas vueltas da cada bucle y cómo se combinan (suma/multiplicación). No basta el resultado.
Ejercicio 2.1 · Cinco fragmentos: bucles simples, anidados y dependientescomplejidad

Para cada método, indica el orden de complejidad con su justificación completa. Si el método tiene mejor y peor caso distintos, analiza ambos.

(a)

public static int c1(int[] a) {
    int suma = 0;
    for (int i = 0; i < a.length; i++) {
        suma += a[i];
    }
    return suma;
}

(b)

public static int c2(int[] a, int x) {
    int veces = 0;
    for (int i = 0; i < a.length; i++) {
        for (int j = 0; j < a.length && a[j] != x; j++) {
            veces++;
        }
    }
    return veces;
}

(c)

public static long c3(int n) {
    long c = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            for (int k = 0; k < n; k++) {
                c++;
            }
        }
    }
    return c;
}

(d)

public static int c4(int n) {
    int c = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            c++;
        }
    }
    return c;
}

(e)

public static int c5(int n) {
    int c = 0;
    for (int j = 1; j <= n; j *= 2) {
        c++;
    }
    return c;
}
Ejercicio 2.2 · Cuatro fragmentos: logarítmicos, trampa geométrica y recursivoscomplejidad

Igual que el anterior. Ojo: dos de estos cuatro se parecen mucho y NO tienen la misma complejidad.

(f)

public static int c6(int n) {
    int c = 0;
    for (int i = 1; i <= n; i *= 2) {
        for (int k = 0; k < n; k++) {
            c++;
        }
    }
    return c;
}

(g)

public static int c7(int n) {
    int c = 0;
    for (int i = 1; i <= n; i *= 2) {
        for (int k = 0; k < i; k++) {
            c++;
        }
    }
    return c;
}

(h)

public static int c8(int n) {
    if (n <= 1) return 0;
    return 1 + c8(n / 2);
}

(i)

public static long c9(int n) {
    if (n <= 1) return n;
    return c9(n - 1) + c9(n - 2);
}

Para (h) e (i): indica también cuántas llamadas recursivas se generan en función de n.

BLOQUE 3 · Métodos de ordenación
Escribir CADA método desde cero (firma dada), explicar cómo funciona y mostrar su traza sobre la secuencia indicada. Indica siempre mejor/medio/peor caso y si es estable.
Ejercicio 3.1 · Burbuja y burbuja mejorada (pregunta del examen real)ordenación
  1. Explica el funcionamiento del método de la Burbuja (2-3 líneas + qué pasa en cada pasada).
  2. Codifica public static void burbuja(int[] a).
  3. Muestra la traza pasada a pasada sobre la secuencia [1, 4, 8, 10, 2].
  4. Detalla dos técnicas de optimización de su rendimiento y codifica la burbuja mejorada que las use. ¿Qué complejidad tiene el mejor caso con ellas y con qué datos se da?
Ejercicio 3.2 · Selecciónordenación
  1. Explica la idea y codifica public static void seleccion(int[] a).
  2. Traza paso a paso sobre [29, 10, 14, 37, 13] (estado del array tras cada colocación).
  3. ¿Por qué la selección es O(n²) incluso si el array ya está ordenado? ¿Qué ventaja tiene en número de intercambios?
Ejercicio 3.3 · Inserciónordenación
  1. Explica la idea (analogía de las cartas) y codifica public static void insercion(int[] a).
  2. Traza sobre [25, 9, 14, 3, 20] mostrando el array tras insertar cada elemento.
  3. ¿Cuál es su mejor caso, con qué datos se da y por qué? ¿Es estable?
Ejercicio 3.4 · Mergesort (mezcla)ordenación
  1. Explica la estrategia "divide y vencerás" de Mergesort.
  2. Codifica mergeSort(int[] a, int ini, int fin) y el método merge, comentando qué hace cada parte.
  3. Traza completa (divisiones y mezclas) sobre [31, 7, 24, 9, 15, 2].
  4. Justifica por qué es O(n log n) en TODOS los casos, qué memoria extra gasta y qué significa que sea estable (señala la línea de tu código que garantiza la estabilidad).
Ejercicio 3.5 · Quicksort (rápido)ordenación
  1. Explica pivote y partición. ¿En qué se diferencia de Mergesort en CUÁNDO se hace el trabajo?
  2. Codifica quickSort(int[] a, int ini, int fin) y particionar con pivote = primer elemento.
  3. Traza sobre [35, 12, 48, 7, 29, 50, 18]: para cada partición indica menores, pivote colocado y mayores.
  4. ¿Cuál es su peor caso, qué datos lo provocan y cómo se evita en la práctica?
Ejercicio 3.6 · Heapsort (montículo)ordenación
  1. Define qué es un max-heap y cómo se guarda en un array (posiciones de los hijos de i).
  2. Codifica heapSort(int[] a) con su método auxiliar hundir, explicando las dos fases.
  3. Traza sobre [19, 4, 27, 11, 8]: montículo construido y array tras cada extracción del máximo.
  4. Complejidad de cada fase y total. ¿Qué ventaja tiene sobre Mergesort?
Ejercicio 3.7 · Shellsortordenación
  1. Explica en qué mejora a la inserción y qué son los gaps.
  2. Codifica shellSort(int[] a) con gaps n/2, n/4, ..., 1.
  3. Traza sobre [45, 23, 67, 12, 38, 7] mostrando el array tras cada pase de gap.
BLOQUE 4 · Estructuras de datos
Elegir la estructura adecuada justificando, e implementar pilas, colas, listas enlazadas, árboles y mapas.
Ejercicio 4.1 · Elección de estructura (pregunta del examen real)teoría razonada

Un módulo de software requiere almacenar un registro dinámico de identificadores únicos (sin duplicados). Es un requisito estricto que, tras cada inserción, la colección quede ordenada automáticamente y que insertar y comprobar existencia sea O(log n). ¿Qué estructura del framework de Java debe seleccionarse?

a) ArrayList   b) HashSet   c) LinkedList   d) TreeSet

  1. Elige y justifica. Explica por qué cada una de las otras tres NO es válida (sin esto no hay máxima puntuación).
  2. Escribe un fragmento de código que demuestre con tu elección: que rechaza duplicados, que queda ordenada sola y cómo consultar el menor y el mayor.
Ejercicio 4.2 · Implementar una pila (LIFO) + aplicaciónestructuras
  1. Implementa una clase PilaArray sobre un array de int: atributos privados (datos, tope), constructor con capacidad, y métodos push, pop, peek, estaVacia, estaLlena, lanzando RuntimeException en pila vacía/llena. Indica la complejidad de cada operación.
  2. Escribe public static boolean equilibrada(String expr) que use una pila (ArrayDeque<Character>) para comprobar si (), [] y {} están equilibrados. Casos: "{[()]}"→true, "([)]"→false, "((("→false.
  3. Explica en 2-3 líneas por qué una pila es LA estructura adecuada para este problema.
Ejercicio 4.3 · Implementar una cola (FIFO) con lista enlazadaestructuras
  1. Implementa ColaEnlazada con una clase interna Nodo y referencias primero y ultimo: métodos encolar, desencolar, frente, estaVacia, tamano. TODAS las operaciones deben ser O(1): explica qué papel juega ultimo para lograrlo y qué caso especial hay al encolar en vacía y al desencolar la última.
  2. Traza: sobre una cola vacía se ejecuta encolar("Ana"), encolar("Luis"), desencolar(), encolar("Marta"), encolar("Pedro"), frente(), desencolar(), desencolar(). Di qué devuelve cada operación y el contenido final.
  3. ¿Qué significan LIFO y FIFO y qué estructura corresponde a cada uno?
Ejercicio 4.4 · Lista doblemente enlazada (pregunta del examen real)estructuras
  1. Expón la lógica de gestión de referencias necesaria para insertar un nuevo nodo en una lista doblemente enlazada (¿cuántas "flechas" hay que tocar y en qué orden? ¿qué casos especiales existen?).
  2. Escribe las clases Nodo (dato, anterior, siguiente) y ListaDoble (referencias primero y ultimo) con: insertarPrincipio, insertarFinal, insertarDespuesDe(int ref, int valor) y eliminar(int valor).
  3. Métodos imprimirAdelante() e imprimirAtras() (este último recorre con anterior desde ultimo: sirve para comprobar que las flechas de vuelta están bien).
  4. Un main que inserte 10, 20, 40 al final, inserte 30 después del 20, inserte 5 al principio, imprima en ambos sentidos, elimine el 40 y vuelva a imprimir.
Ejercicio 4.5 · Árbol binario de búsqueda: insertar, recorrer, buscarestructuras
  1. Implementa una clase ABB (con clase interna Nodo): método público insertar(int) apoyado en un privado recursivo (sin duplicados), y los tres recorridos preorden, inorden y postorden (patrón público lanzadera + privado recursivo con caso base).
  2. Implementa buscar(int) aprovechando el orden del ABB (que imprima los nodos que visita).
  3. Inserta, en este orden: 45, 23, 67, 12, 38, 51, 89, 30. Dibuja el árbol y escribe los tres recorridos.
  4. ¿Qué propiedad especial tiene el inorden de un ABB? ¿Qué camino sigue la búsqueda de 30? ¿Qué complejidad tiene buscar en este árbol, y cuál tendría si los 8 valores se hubieran insertado en orden creciente? Justifica.
Ejercicio 4.6 · HashMap: contar frecuenciasestructuras
  1. Escribe public static Map<String,Integer> frecuencias(String[] nombres) que devuelva cuántas veces aparece cada nombre (usa getOrDefault), y el código que recorre el mapa imprimiendo nombre aparece N veces.
  2. ¿Por qué un Map y no dos ArrayList paralelos? ¿Qué complejidad tienen put y get en un HashMap y gracias a qué mecanismo interno? ¿Qué es una colisión y cómo se resuelve?
BLOQUE 5 · Búsqueda y recursión
Búsqueda lineal y binaria (iterativa y recursiva) y métodos recursivos básicos con su traza.
Ejercicio 5.1 · Búsqueda lineal y binaria iterativabúsqueda
  1. Codifica busquedaLineal(int[] a, int x) y busquedaBinaria(int[] a, int x) (iterativa, devolviendo la posición o -1).
  2. Sobre [2, 5, 8, 12, 16, 23, 38, 56, 72, 91], escribe qué posiciones examina la binaria al buscar 23 y al buscar 7 (traza de ini, fin, mid).
  3. ¿Por qué la binaria exige el array ordenado? Justifica su O(log n): ¿cuántas comparaciones necesita como máximo con n = 1000?
Ejercicio 5.2 · Búsqueda binaria recursivabúsqueda + recursión
  1. Codifica busquedaBinariaRec(int[] a, int x, int ini, int fin). Señala sus DOS casos base.
  2. ¿Qué devuelve tu método si el array no estuviera ordenado? ¿Detecta el error?
Ejercicio 5.3 · Recursión: factorial y suma de un arrayrecursión
  1. Codifica factorial(int n) recursivo señalando caso base y caso recursivo.
  2. Escribe la traza de llamadas de factorial(4): qué se apila y qué devuelve cada llamada al deshacerse.
  3. Codifica sumaArray(int[] a, int i): suma recursiva de los elementos (el elemento i + la suma del resto).
  4. ¿Qué ocurre si olvidas el caso base? ¿Qué estructura de datos usa Java para gestionar las llamadas?