Módulo 08: Arrays y algoritmos

Objetivos del módulo

  • Entender qué es un array y cómo se guarda en memoria
  • Declarar, crear, inicializar y recorrer arrays con for y for-each
  • Evitar la excepción ArrayIndexOutOfBoundsException
  • Pasar arrays a métodos y devolverlos, sabiendo que se trabaja con referencias
  • Distinguir entre copiar un array y copiar su referencia
  • Usar la clase Arrays: toString, sort, fill, equals, binarySearch, copyOf
  • Programar los algoritmos básicos: suma, media, máximo, mínimo, contar, invertir y desplazar
  • Implementar la búsqueda lineal y la binaria
  • Implementar y comparar los algoritmos de ordenación de burbuja, selección e inserción
  • Trabajar con arrays bidimensionales (matrices) y matrices irregulares

1. ¿Qué es un array?

Imagina que tienes que guardar las notas de 30 alumnos. Con lo que sabes, necesitarías 30 variables (nota1, nota2… nota30) y no podrías recorrerlas con un bucle. Si además el número de alumnos cambia cada curso, el problema no tiene solución con variables sueltas.

📘 Concepto: Un array (también llamado vector o arreglo) es una estructura que guarda varios valores del mismo tipo bajo un único nombre. Cada valor ocupa una posición numerada con un índice, que empieza en 0. Su tamaño se fija al crearlo y ya no puede cambiar.

Piensa en un casillero de un gimnasio: una fila de taquillas iguales, numeradas, una al lado de otra. Para usar una basta con saber su número.

Variable notas en la pila con una referencia a un array de cinco enteros en el montón, con sus índices de 0 a 4

Figura 1. Un array en memoria: la variable guarda una referencia a los elementos, que están seguidos en el montón.

Tres ideas clave de la figura:

  1. Los elementos están contiguos en memoria, por eso acceder a notas[i] es inmediato, sea cual sea i.
  2. Si el array tiene n elementos, los índices van de 0 a n − 1. notas.length vale 5 y el último elemento es notas[4].
  3. La variable notas no contiene los números: contiene una referencia (la dirección) del array, que vive en una zona de memoria llamada montón (heap). Esto tiene consecuencias importantes al asignar arrays y pasarlos a métodos (secciones 4 y 5).

2. Declarar, crear e inicializar

int[] notas;                 // 1) declarar: notas podrá referenciar un array de int
notas = new int[5];          // 2) crear: reserva 5 enteros, todos a 0
int[] edades = new int[30];  // declarar y crear en una línea
int[] primos = {2, 3, 5, 7}; // declarar, crear e inicializar con valores
primos = new int[] {11, 13}; // en una asignación posterior hay que escribir new int[]

💡 Consejo: Java también acepta int notas[] (estilo heredado de C), pero la forma recomendada es int[] notas: el tipo es «array de int» y queda junto.

import java.util.Arrays;

public class ArrayBasico {
    public static void main(String[] args) {
        // 1) Declarar y crear: 5 enteros, todos a 0
        int[] notas = new int[5];
        notas[0] = 7;                       // asignar por índice
        notas[1] = 4;
        notas[4] = 9;                       // el último índice es length - 1
        System.out.println("notas: " + Arrays.toString(notas));
        System.out.println("Longitud: " + notas.length);
        System.out.println("Primer elemento: " + notas[0]);
        System.out.println("Último elemento: " + notas[notas.length - 1]);

        // 2) Declarar, crear e inicializar a la vez
        double[] precios = {19.99, 5.50, 3.25};
        String[] dias = {"lunes", "martes", "miércoles"};
        System.out.println("precios: " + Arrays.toString(precios));
        System.out.println("dias: " + Arrays.toString(dias));

        // 3) Valores por defecto
        System.out.println("double[]: " + Arrays.toString(new double[3]));
        System.out.println("boolean[]: " + Arrays.toString(new boolean[3]));
        char[] letras = new char[3];
        System.out.println("char[]: código del primero = " + (int) letras[0]);
        System.out.println("String[]: " + Arrays.toString(new String[3]));

        // 4) Imprimir el array directamente NO muestra su contenido
        System.out.println("println(notas): " + notas);
    }
}
notas: [7, 4, 0, 0, 9]
Longitud: 5
Primer elemento: 7
Último elemento: 9
precios: [19.99, 5.5, 3.25]
dias: [lunes, martes, miércoles]
double[]: [0.0, 0.0, 0.0]
boolean[]: [false, false, false]
char[]: código del primero = 0
String[]: [null, null, null]
println(notas): [I@7ad041f3

Observa que:

  • Al crear un array con new, sus elementos toman valores por defecto: 0 para los números, false para boolean, el carácter de código 0 para char y null (ninguna referencia) para los objetos como String.
  • length es un atributo, no un método: se escribe sin paréntesis (notas.length), al contrario que en String (texto.length()).
  • Imprimir el array directamente muestra algo como [I@7ad041f3: el tipo ([I significa «array de int») y un código que identifica el objeto. Para ver el contenido usa Arrays.toString(array).

3. Recorrer arrays

Un array y un bucle for son la pareja perfecta: el índice del bucle recorre las posiciones de 0 a length - 1.

for (int i = 0; i < v.length; i++) {      // < y no <=
    System.out.println(v[i]);
}

3.1. El bucle for-each

Desde Java 5 existe un bucle especial para recorrer todos los elementos de un array sin manejar índices, el for-each (o for mejorado). Se lee «para cada t de temp»:

for (double t : temp) {
    suma += t;
}
  for clásico for-each
Sintaxis for (int i = 0; i < v.length; i++) for (int x : v)
Acceso al índice Sí (i) No
Puede modificar el array Sí (v[i] = ...) No: x es una copia del elemento
Recorrer al revés, saltando, una parte Sí No: siempre todo, de principio a fin
Riesgo de salirse del array Sí No

Regla práctica: usa for-each cuando solo necesites leer todos los valores; usa el for clásico cuando necesites la posición o quieras modificar el array.

Este programa muestra por qué los arrays son imprescindibles: para saber qué días superan la media, hay que guardar todas las temperaturas, porque la media no se conoce hasta haberlas leído todas.

import java.util.Scanner;

public class TemperaturasArray {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("¿Cuántos días? ");
        int n = sc.nextInt();
        double[] temp = new double[n];          // el tamaño puede decidirse al ejecutar

        // Lectura
        for (int i = 0; i < temp.length; i++) {
            System.out.print("Temperatura del día " + (i + 1) + ": ");
            temp[i] = sc.nextDouble();
        }

        // Media (for-each: solo necesitamos los valores)
        double suma = 0;
        for (double t : temp) {
            suma += t;
        }
        double media = suma / temp.length;
        System.out.printf("Media: %.2f%n", media);

        // Días por encima de la media (for clásico: necesitamos el índice)
        System.out.print("Días por encima de la media:");
        for (int i = 0; i < temp.length; i++) {
            if (temp[i] > media) {
                System.out.print(" " + (i + 1));
            }
        }
        System.out.println();

        // Recorrido inverso
        System.out.print("Del último al primero:");
        for (int i = temp.length - 1; i >= 0; i--) {
            System.out.print(" " + temp[i]);
        }
        System.out.println();
    }
}
¿Cuántos días? 5
Temperatura del día 1: 21.5
Temperatura del día 2: 24
Temperatura del día 3: 19
Temperatura del día 4: 26.5
Temperatura del día 5: 22
Media: 22.60
Días por encima de la media: 2 4
Del último al primero: 22.0 26.5 19.0 24.0 21.5

3.2. ArrayIndexOutOfBoundsException

Si usas un índice negativo o mayor o igual que length, Java detiene el programa con una excepción. El error más típico es escribir <= en lugar de < en el bucle:

public class FueraDeRango {
    public static void main(String[] args) {
        int[] v = {10, 20, 30, 40, 50};
        // Error típico: usar <= en lugar de <
        for (int i = 0; i <= v.length; i++) {
            System.out.println("v[" + i + "] = " + v[i]);
        }
    }
}
v[0] = 10
v[1] = 20
v[2] = 30
v[3] = 40
v[4] = 50
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at FueraDeRango.main(FueraDeRango.java:6)

El mensaje es muy claro: el índice 5 está fuera de los límites para un array de longitud 5, en la línea 6. Aprende a leer estos mensajes: te dicen qué ha pasado y dónde. (Las excepciones se estudian en el módulo 15.)

⚠️ Importante: A diferencia de C, Java siempre comprueba los límites del array. Es una ventaja: en lugar de leer basura de la memoria o corromper otros datos, el programa se detiene en el punto exacto del error.


4. Arrays y métodos

Un array se puede pasar como argumento y se puede devolver como resultado. Como la variable contiene una referencia, al pasarla se copia la referencia (paso por valor, módulo 07) y el método trabaja sobre el mismo array: si lo modifica, el cambio se ve desde fuera.

import java.util.Arrays;

public class ArraysYMetodos {

    // Devuelve un array nuevo con los números de 'desde' a 'hasta'
    static int[] rango(int desde, int hasta) {
        int[] r = new int[hasta - desde + 1];
        for (int i = 0; i < r.length; i++) {
            r[i] = desde + i;
        }
        return r;
    }

    // Recibe una referencia: modifica el array original
    static void multiplicar(int[] v, int factor) {
        for (int i = 0; i < v.length; i++) {
            v[i] *= factor;
        }
    }

    // Solo lee el array y devuelve un resultado
    static double media(int[] v) {
        int suma = 0;
        for (int x : v) {
            suma += x;
        }
        return (double) suma / v.length;
    }

    public static void main(String[] args) {
        int[] numeros = rango(1, 6);
        System.out.println("rango(1, 6): " + Arrays.toString(numeros));
        System.out.println("media: " + media(numeros));
        multiplicar(numeros, 10);
        System.out.println("tras multiplicar por 10: " + Arrays.toString(numeros));
        System.out.println("media de un array literal: " + media(new int[] {4, 8, 15}));
    }
}
rango(1, 6): [1, 2, 3, 4, 5, 6]
media: 3.5
tras multiplicar por 10: [10, 20, 30, 40, 50, 60]
media de un array literal: 9.0
  • rango crea un array nuevo y devuelve su referencia: el array sigue existiendo después de que el método termine, porque está en el montón.
  • multiplicar es void, pero modifica el array original.
  • new int[] {4, 8, 15} crea un array «anónimo» para pasarlo directamente.

💡 Consejo: Deja claro en el nombre y en el Javadoc si un método modifica el array que recibe (ordenar, multiplicar) o si devuelve uno nuevo y deja el original intacto (copiaOrdenada, sinDuplicados). Es una de las fuentes de errores más frecuentes.


5. Copiar arrays frente a copiar referencias

Como las variables de tipo array guardan referencias, la asignación b = a no copia el array: hace que b apunte al mismo array que a. A partir de ahí, a y b son dos nombres (alias) de los mismos datos.

Con b = a dos variables apuntan al mismo array; con clone se crea un segundo array independiente

Figura 2. Asignar una referencia (arriba) frente a copiar el array (abajo).

Para obtener una copia independiente tienes varias opciones:

Forma Qué hace
a.clone() Copia completa
Arrays.copyOf(a, n) Copia los n primeros; si n > a.length, rellena con valores por defecto
Arrays.copyOfRange(a, desde, hasta) Copia de desde (incluido) a hasta (excluido)
System.arraycopy(origen, posOrigen, destino, posDestino, cantidad) Copia en un array ya creado; es la más rápida y flexible
import java.util.Arrays;

public class CopiarArrays {
    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4, 5};

        // 1) Asignar NO copia: b es otro nombre para el mismo array
        int[] b = a;
        b[0] = 99;
        System.out.println("a tras b[0] = 99: " + Arrays.toString(a));
        System.out.println("¿a == b? " + (a == b));
        a[0] = 1;                                   // lo dejamos como estaba

        // 2) Copias reales (arrays nuevos)
        int[] c = a.clone();
        int[] d = Arrays.copyOf(a, a.length);
        int[] e = Arrays.copyOf(a, 8);             // más largo: rellena con 0
        int[] f = Arrays.copyOfRange(a, 1, 4);     // posiciones 1, 2 y 3
        int[] g = new int[a.length];
        System.arraycopy(a, 0, g, 0, a.length);    // origen, desde, destino, desde, cuántos

        c[0] = -1;
        System.out.println("a tras c[0] = -1: " + Arrays.toString(a));
        System.out.println("c: " + Arrays.toString(c));
        System.out.println("e: " + Arrays.toString(e));
        System.out.println("f: " + Arrays.toString(f));

        // 3) Comparar: == compara referencias; Arrays.equals, contenidos
        System.out.println("¿a == g? " + (a == g));
        System.out.println("¿Arrays.equals(a, g)? " + Arrays.equals(a, g));
        System.out.println("¿Arrays.equals(a, d)? " + Arrays.equals(a, d));
    }
}
a tras b[0] = 99: [99, 2, 3, 4, 5]
¿a == b? true
a tras c[0] = -1: [1, 2, 3, 4, 5]
c: [-1, 2, 3, 4, 5]
e: [1, 2, 3, 4, 5, 0, 0, 0]
f: [2, 3, 4]
¿a == g? false
¿Arrays.equals(a, g)? true
¿Arrays.equals(a, d)? true

⚠️ Importante: Lo mismo pasa al comparar: a == b compara referencias (¿es el mismo array?), mientras que Arrays.equals(a, b) compara el contenido elemento a elemento. Es la misma diferencia que entre == y equals con los String (módulo 05).


6. La clase Arrays

La clase java.util.Arrays (hay que importarla) ofrece métodos static para las tareas más comunes:

Método Qué hace
Arrays.toString(v) Texto con el contenido: [3, 7, 11]
Arrays.deepToString(m) Lo mismo para arrays bidimensionales
Arrays.sort(v) Ordena de menor a mayor (modifica v)
Arrays.sort(v, desde, hasta) Ordena solo una parte
Arrays.fill(v, valor) Pone todos los elementos a valor
Arrays.equals(a, b) true si tienen la misma longitud y los mismos elementos en el mismo orden
Arrays.binarySearch(v, clave) Busca en un array ordenado; devuelve la posición o un número negativo
Arrays.copyOf, copyOfRange Copias (sección 5)
import java.util.Arrays;

public class ClaseArrays {
    public static void main(String[] args) {
        int[] v = {42, 7, 19, 3, 25, 11};
        System.out.println("Original:  " + Arrays.toString(v));

        Arrays.sort(v);                                   // ordena de menor a mayor
        System.out.println("Ordenado:  " + Arrays.toString(v));

        // binarySearch: SOLO sobre arrays ordenados
        System.out.println("binarySearch(19): " + Arrays.binarySearch(v, 19));
        System.out.println("binarySearch(20): " + Arrays.binarySearch(v, 20));

        int[] ceros = new int[6];
        Arrays.fill(ceros, 5);                            // todos los elementos a 5
        System.out.println("fill(5):   " + Arrays.toString(ceros));

        String[] nombres = {"Marta", "ana", "Luis", "Óscar", "Bea"};
        Arrays.sort(nombres);                             // orden de los códigos Unicode
        System.out.println("Nombres:   " + Arrays.toString(nombres));

        int[] copia = {3, 7, 11, 19, 25, 42};
        System.out.println("equals:    " + Arrays.equals(v, copia));
    }
}
Original:  [42, 7, 19, 3, 25, 11]
Ordenado:  [3, 7, 11, 19, 25, 42]
binarySearch(19): 3
binarySearch(20): -5
fill(5):   [5, 5, 5, 5, 5, 5]
Nombres:   [Bea, Luis, Marta, ana, Óscar]
equals:    true

Dos detalles que conviene entender:

  • binarySearch(20) devuelve -5. Cuando no encuentra el valor, devuelve -(punto de inserción) - 1, donde el punto de inserción es la posición en que debería estar (la 4, entre el 19 y el 25). Lo importante: negativo = no está.
  • Los String se ordenan por los códigos Unicode de sus caracteres: todas las mayúsculas van antes que las minúsculas y las letras con tilde, después de todas las demás. Por eso "ana" va detrás de "Marta" y "Óscar", al final. Verás cómo ordenar alfabéticamente «de verdad» en los módulos 09 y 16.

📘 Concepto: Arrays.sort no usa ninguno de los algoritmos sencillos que vas a programar en este módulo. Para tipos primitivos usa un Dual-Pivot Quicksort y para objetos, TimSort (una mezcla de mergesort e inserción). Ambos son mucho más rápidos que la burbuja con muchos datos. En la práctica profesional, usa Arrays.sort; programar tú los algoritmos sirve para aprender a pensar.


7. Algoritmos básicos con arrays

import java.util.Arrays;

public class AlgoritmosBasicos {
    public static void main(String[] args) {
        int[] v = {12, 45, 7, 23, 56, 89, 34};

        // Suma y media
        int suma = 0;
        for (int x : v) {
            suma += x;
        }
        double media = (double) suma / v.length;
        System.out.printf("Suma: %d   Media: %.2f%n", suma, media);

        // Máximo y mínimo con su posición
        int posMax = 0, posMin = 0;
        for (int i = 1; i < v.length; i++) {
            if (v[i] > v[posMax]) {
                posMax = i;
            }
            if (v[i] < v[posMin]) {
                posMin = i;
            }
        }
        System.out.println("Máximo: " + v[posMax] + " (posición " + posMax + ")");
        System.out.println("Mínimo: " + v[posMin] + " (posición " + posMin + ")");

        // Contar los que superan la media
        int mayores = 0;
        for (int x : v) {
            if (x > media) {
                mayores++;
            }
        }
        System.out.println("Superan la media: " + mayores);

        // Invertir "en el sitio" con dos índices que se cruzan
        for (int i = 0, j = v.length - 1; i < j; i++, j--) {
            int aux = v[i];
            v[i] = v[j];
            v[j] = aux;
        }
        System.out.println("Invertido:            " + Arrays.toString(v));

        // Desplazar (rotar) una posición a la derecha: el último pasa al principio
        int ultimo = v[v.length - 1];
        for (int i = v.length - 1; i > 0; i--) {
            v[i] = v[i - 1];
        }
        v[0] = ultimo;
        System.out.println("Rotado a la derecha:  " + Arrays.toString(v));

        // Desplazar una posición a la izquierda: el primero pasa al final
        int primero = v[0];
        for (int i = 0; i < v.length - 1; i++) {
            v[i] = v[i + 1];
        }
        v[v.length - 1] = primero;
        System.out.println("Rotado a la izquierda: " + Arrays.toString(v));
    }
}
Suma: 266   Media: 38.00
Máximo: 89 (posición 5)
Mínimo: 7 (posición 2)
Superan la media: 3
Invertido:            [34, 89, 56, 23, 7, 45, 12]
Rotado a la derecha:  [12, 34, 89, 56, 23, 7, 45]
Rotado a la izquierda: [34, 89, 56, 23, 7, 45, 12]

Puntos clave de cada algoritmo:

Algoritmo Idea
Suma / media Acumulador recorriendo todos los elementos; cuidado con la división entera
Máximo / mínimo Guardar la posición (empezando en 0) en lugar del valor: así tienes las dos cosas
Contar Contador dentro de un if
Invertir Dos índices, i desde el principio y j desde el final, intercambiando hasta que se crucen
Desplazar a la derecha Guardar el último, mover cada elemento a la posición siguiente empezando por el final y poner el guardado en la 0
Desplazar a la izquierda Guardar el primero, mover cada elemento a la anterior empezando por el principio y poner el guardado al final

⚠️ Importante: Al desplazar, el sentido del bucle es crucial. Si para rotar a la derecha recorrieras de izquierda a derecha (v[i + 1] = v[i]), copiarías el primer valor en todas las posiciones. Haz una traza con papel si no lo ves claro.


8. Búsqueda

8.1. Búsqueda lineal (secuencial)

Recorre el array desde el principio hasta encontrar el valor o llegar al final. Funciona con cualquier array, ordenado o no. En el peor caso (el valor no está o es el último) hace n comparaciones.

8.2. Búsqueda binaria (dicotómica)

Si el array está ordenado, se puede hacer muchísimo mejor: se compara con el elemento central y se descarta la mitad en la que el valor no puede estar. Es lo que haces al buscar una palabra en un diccionario de papel. Ya viste la versión recursiva en el módulo 07; esta es la iterativa:

public class Busquedas {

    // Búsqueda lineal: recorre hasta encontrarlo. Devuelve la posición o -1
    static int busquedaLineal(int[] v, int clave) {
        for (int i = 0; i < v.length; i++) {
            if (v[i] == clave) {
                return i;
            }
        }
        return -1;
    }

    // Búsqueda binaria iterativa: el array DEBE estar ordenado
    static int busquedaBinaria(int[] v, int clave) {
        int izq = 0, der = v.length - 1;
        int paso = 1;
        while (izq <= der) {
            int medio = (izq + der) / 2;
            System.out.printf("  paso %d: izq=%d der=%d medio=%d v[medio]=%d%n",
                    paso++, izq, der, medio, v[medio]);
            if (v[medio] == clave) {
                return medio;
            } else if (v[medio] < clave) {
                izq = medio + 1;          // descartamos la mitad izquierda
            } else {
                der = medio - 1;          // descartamos la mitad derecha
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        int[] desordenado = {34, 7, 91, 15, 63, 3};
        System.out.println("Lineal, buscar 15: " + busquedaLineal(desordenado, 15));
        System.out.println("Lineal, buscar 50: " + busquedaLineal(desordenado, 50));

        int[] ordenado = {3, 8, 15, 21, 34, 42, 57, 63, 78, 91};
        System.out.println("Binaria, buscar 57:");
        System.out.println("  posición " + busquedaBinaria(ordenado, 57));
        System.out.println("Binaria, buscar 10:");
        System.out.println("  posición " + busquedaBinaria(ordenado, 10));
    }
}
Lineal, buscar 15: 3
Lineal, buscar 50: -1
Binaria, buscar 57:
  paso 1: izq=0 der=9 medio=4 v[medio]=34
  paso 2: izq=5 der=9 medio=7 v[medio]=63
  paso 3: izq=5 der=6 medio=5 v[medio]=42
  paso 4: izq=6 der=6 medio=6 v[medio]=57
  posición 6
Binaria, buscar 10:
  paso 1: izq=0 der=9 medio=4 v[medio]=34
  paso 2: izq=0 der=3 medio=1 v[medio]=8
  paso 3: izq=2 der=3 medio=2 v[medio]=15
  posición -1

Cuatro pasos de la búsqueda binaria de 57: en cada uno se marca el elemento central y se descarta la mitad que no puede contenerlo

Figura 3. Búsqueda binaria paso a paso: cada comparación descarta la mitad de los candidatos.

Elementos Búsqueda lineal (peor caso) Búsqueda binaria (peor caso)
10 10 comparaciones 4
1 000 1 000 10
1 000 000 1 000 000 20
1 000 000 000 1 000 000 000 30

📘 Concepto: La complejidad de un algoritmo mide cómo crece su tiempo de ejecución al crecer el tamaño n de los datos. Se expresa con la notación O grande: la búsqueda lineal es O(n) (el doble de datos, el doble de tiempo) y la binaria es O(log n) (el doble de datos, una comparación más). Esta diferencia es la razón por la que vale la pena mantener datos ordenados.

💡 Consejo: Con arrays enormes, (izq + der) / 2 puede desbordar el int si la suma supera 2 147 483 647. La forma segura es izq + (der - izq) / 2. Este fallo estuvo años en la propia biblioteca de Java hasta que se corrigió en 2006.


9. Ordenación

Ordenar es una de las tareas más estudiadas de la informática. Vamos a ver los tres algoritmos sencillos clásicos, todos con el mismo array de partida, [5, 2, 9, 1, 6].

9.1. Burbuja (bubble sort)

Se recorre el array comparando cada elemento con su vecino y se intercambian si están desordenados. En cada pasada, el mayor de los que quedan «sube» hasta el final como una burbuja, así que cada pasada recorre un elemento menos. Si una pasada no hace ningún intercambio, el array ya está ordenado y se puede parar.

Burbuja: detalle de las comparaciones de la primera pasada y estado del array tras cada pasada

Figura 4. Ordenación por burbuja: en cada pasada el mayor de los pendientes llega a su sitio.

import java.util.Arrays;

public class Burbuja {

    static void burbuja(int[] v) {
        for (int pasada = 1; pasada < v.length; pasada++) {
            boolean hayCambios = false;
            // en cada pasada, el mayor de los que quedan "sube" al final
            for (int i = 0; i < v.length - pasada; i++) {
                if (v[i] > v[i + 1]) {
                    int aux = v[i];
                    v[i] = v[i + 1];
                    v[i + 1] = aux;
                    hayCambios = true;
                }
            }
            System.out.println("Pasada " + pasada + ": " + Arrays.toString(v));
            if (!hayCambios) {
                break;                 // ya está ordenado: no hace falta seguir
            }
        }
    }

    public static void main(String[] args) {
        int[] v = {5, 2, 9, 1, 6};
        System.out.println("Inicial:  " + Arrays.toString(v));
        burbuja(v);
    }
}
Inicial:  [5, 2, 9, 1, 6]
Pasada 1: [2, 5, 1, 6, 9]
Pasada 2: [2, 1, 5, 6, 9]
Pasada 3: [1, 2, 5, 6, 9]
Pasada 4: [1, 2, 5, 6, 9]

En este ejemplo la pasada 3 aún hizo un intercambio (el 2 con el 1), así que hizo falta la pasada 4 para comprobar que ya no quedaba nada por hacer. Con un array casi ordenado, la bandera hayCambios ahorra muchas pasadas.

9.2. Selección (selection sort)

Se busca el mínimo de todo el array y se intercambia con el primero; luego el mínimo del resto se intercambia con el segundo, y así sucesivamente. La parte izquierda va quedando ordenada.

Selección: en cada paso se busca el mínimo de la parte desordenada y se intercambia con la posición i

Figura 5. Ordenación por selección: como mucho un intercambio por vuelta.

import java.util.Arrays;

public class Seleccion {

    static void seleccion(int[] v) {
        for (int i = 0; i < v.length - 1; i++) {
            int posMin = i;
            for (int j = i + 1; j < v.length; j++) {   // buscar el mínimo del resto
                if (v[j] < v[posMin]) {
                    posMin = j;
                }
            }
            int aux = v[i];                            // colocarlo en la posición i
            v[i] = v[posMin];
            v[posMin] = aux;
            System.out.println("i = " + i + " (mínimo " + v[i] + "): " + Arrays.toString(v));
        }
    }

    public static void main(String[] args) {
        int[] v = {5, 2, 9, 1, 6};
        System.out.println("Inicial:            " + Arrays.toString(v));
        seleccion(v);
    }
}
Inicial:            [5, 2, 9, 1, 6]
i = 0 (mínimo 1): [1, 2, 9, 5, 6]
i = 1 (mínimo 2): [1, 2, 9, 5, 6]
i = 2 (mínimo 5): [1, 2, 5, 9, 6]
i = 3 (mínimo 6): [1, 2, 5, 6, 9]

9.3. Inserción (insertion sort)

Es como ordenar las cartas de una mano: tomas cada carta y la insertas en su lugar entre las que ya tienes ordenadas, desplazando a la derecha las mayores para hacerle hueco.

Inserción: cada elemento se inserta en su sitio dentro de la parte ordenada de la izquierda

Figura 6. Ordenación por inserción: la parte ordenada crece de uno en uno.

import java.util.Arrays;

public class Insercion {

    static void insercion(int[] v) {
        for (int i = 1; i < v.length; i++) {
            int actual = v[i];          // elemento que vamos a insertar
            int j = i - 1;
            while (j >= 0 && v[j] > actual) {
                v[j + 1] = v[j];        // desplazar a la derecha los mayores
                j--;
            }
            v[j + 1] = actual;          // insertar en el hueco
            System.out.println("i = " + i + " (inserta " + actual + "): " + Arrays.toString(v));
        }
    }

    public static void main(String[] args) {
        int[] v = {5, 2, 9, 1, 6};
        System.out.println("Inicial:             " + Arrays.toString(v));
        insercion(v);
    }
}
Inicial:             [5, 2, 9, 1, 6]
i = 1 (inserta 2): [2, 5, 9, 1, 6]
i = 2 (inserta 9): [2, 5, 9, 1, 6]
i = 3 (inserta 1): [1, 2, 5, 9, 6]
i = 4 (inserta 6): [1, 2, 5, 6, 9]

9.4. Comparativa

Algoritmo Peor caso Mejor caso Intercambios Cuándo usarlo
Burbuja (con bandera) O(n²) O(n) si ya está ordenado Muchos Solo para aprender
Selección O(n²) O(n²) siempre Como mucho n − 1 Cuando mover datos es caro
Inserción O(n²) O(n) si está casi ordenado Desplazamientos Arrays pequeños o casi ordenados
Quicksort O(n²) (raro) O(n log n) — Uso general (medio: O(n log n))
Mergesort O(n log n) O(n log n) — Cuando se necesita estabilidad o un tiempo garantizado; usa memoria extra

O(n²) significa que si duplicas los datos, el tiempo se multiplica por cuatro. Con 10 000 elementos, un algoritmo O(n²) hace del orden de 100 millones de operaciones; uno O(n log n), unas 130 000.

📘 Concepto: Quicksort elige un elemento pivote, coloca a su izquierda los menores y a su derecha los mayores, y ordena recursivamente cada parte. Mergesort divide el array en dos mitades, ordena cada una recursivamente y las mezcla (como en el ejercicio 3). Los dos aplican la estrategia de divide y vencerás con recursividad (módulo 07) y los estudiarás en asignaturas de algoritmos y estructuras de datos.


10. Arrays bidimensionales (matrices)

Un array bidimensional organiza los datos en filas y columnas, como una hoja de cálculo, un tablero de ajedrez o una imagen. Cada elemento se identifica con dos índices: m[fila][columna].

Matriz de 3 filas y 4 columnas con el elemento de la fila 1 y columna 2 resaltado, y su representación real como un array de tres referencias a arrays de cuatro enteros

Figura 7. Una matriz es, en realidad, un array de arrays.

int[][] m = new int[3][4];          // 3 filas, 4 columnas, todo a 0
int[][] n = {{1, 2, 3}, {4, 5, 6}}; // 2 filas, 3 columnas
m[1][2] = 7;                        // fila 1, columna 2
int filas = m.length;               // 3
int columnas = m[0].length;         // 4 (longitud de la fila 0)

Para recorrer una matriz se usan dos bucles anidados (módulo 06): el externo para las filas y el interno para las columnas.

public class Matrices {

    static void mostrar(int[][] m) {
        for (int f = 0; f < m.length; f++) {
            for (int c = 0; c < m[f].length; c++) {
                System.out.printf("%4d", m[f][c]);
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[][] m = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12}
        };
        System.out.println("Filas: " + m.length + ", columnas: " + m[0].length);
        System.out.println("m[1][2] = " + m[1][2]);
        mostrar(m);

        // Suma de cada fila
        for (int f = 0; f < m.length; f++) {
            int suma = 0;
            for (int c = 0; c < m[f].length; c++) {
                suma += m[f][c];
            }
            System.out.println("Suma fila " + f + ": " + suma);
        }

        // Suma de cada columna: el bucle externo recorre columnas
        for (int c = 0; c < m[0].length; c++) {
            int suma = 0;
            for (int f = 0; f < m.length; f++) {
                suma += m[f][c];
            }
            System.out.println("Suma columna " + c + ": " + suma);
        }

        // Traspuesta: filas <-> columnas (4x3)
        int[][] t = new int[m[0].length][m.length];
        for (int f = 0; f < m.length; f++) {
            for (int c = 0; c < m[0].length; c++) {
                t[c][f] = m[f][c];
            }
        }
        System.out.println("Traspuesta:");
        mostrar(t);
    }
}
Filas: 3, columnas: 4
m[1][2] = 7
   1   2   3   4
   5   6   7   8
   9  10  11  12
Suma fila 0: 10
Suma fila 1: 26
Suma fila 2: 42
Suma columna 0: 15
Suma columna 1: 18
Suma columna 2: 21
Suma columna 3: 24
Traspuesta:
   1   5   9
   2   6  10
   3   7  11
   4   8  12
  • Para sumar las columnas, se intercambia el papel de los bucles: el externo recorre columnas y el interno, filas.
  • La traspuesta de una matriz f × c es una matriz c × f en la que t[c][f] = m[f][c].

10.1. Diagonales de una matriz cuadrada

En una matriz cuadrada de tamaño n, la diagonal principal son las posiciones [i][i] y la secundaria, las posiciones [i][n - 1 - i]. Basta con un bucle simple:

public class Diagonales {
    public static void main(String[] args) {
        int[][] m = {
            {2, 7, 6},
            {9, 5, 1},
            {4, 3, 8}
        };
        int n = m.length;
        int principal = 0, secundaria = 0;
        for (int i = 0; i < n; i++) {
            principal += m[i][i];             // posiciones (0,0) (1,1) (2,2)
            secundaria += m[i][n - 1 - i];    // posiciones (0,2) (1,1) (2,0)
        }
        System.out.println("Diagonal principal: " + principal);
        System.out.println("Diagonal secundaria: " + secundaria);
    }
}
Diagonal principal: 15
Diagonal secundaria: 15

10.2. Matrices irregulares

Como cada fila es un array independiente, las filas pueden tener longitudes distintas. Se crea primero el array de filas (new int[filas][]) y después cada fila con su tamaño:

import java.util.Arrays;

public class Irregular {
    public static void main(String[] args) {
        // Triángulo de Pascal: la fila f tiene f + 1 elementos
        int filas = 6;
        int[][] pascal = new int[filas][];          // solo se fija el número de filas
        for (int f = 0; f < filas; f++) {
            pascal[f] = new int[f + 1];             // cada fila con su longitud
            pascal[f][0] = 1;
            pascal[f][f] = 1;
            for (int c = 1; c < f; c++) {
                pascal[f][c] = pascal[f - 1][c - 1] + pascal[f - 1][c];
            }
        }
        for (int[] fila : pascal) {                 // for-each sobre las filas
            System.out.println(Arrays.toString(fila));
        }
        System.out.println(Arrays.deepToString(new int[][] {{1, 2}, {3}}));
    }
}
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[[1, 2], [3]]

⚠️ Importante: En una matriz irregular, m[0].length no sirve para todas las filas. Recorre siempre con m[f].length, como en el método mostrar de Matrices, y tu código funcionará con matrices regulares e irregulares. Arrays.deepToString muestra cualquier array bidimensional en una línea.


11. Arrays de String

Un array puede contener objetos, como String. Funciona igual, con dos diferencias: los elementos sin asignar valen null, y para comparar hay que usar equals.

import java.util.Scanner;

public class ArrayStrings {
    public static void main(String[] args) {
        String[] meses = {"enero", "febrero", "marzo", "abril", "mayo", "junio",
                "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"};

        // Traducir número -> nombre sin switch
        int mes = 10;
        System.out.println("El mes " + mes + " es " + meses[mes - 1]);

        // Buscar un texto: con equals, nunca con ==
        Scanner sc = new Scanner(System.in);
        System.out.print("Escribe un mes: ");
        String buscado = sc.next().toLowerCase();
        int pos = -1;
        for (int i = 0; i < meses.length && pos == -1; i++) {
            if (meses[i].equals(buscado)) {
                pos = i;
            }
        }
        System.out.println(pos == -1 ? "No es un mes" : buscado + " es el mes " + (pos + 1));

        // El mes con el nombre más largo
        String masLargo = meses[0];
        for (String m : meses) {
            if (m.length() > masLargo.length()) {
                masLargo = m;
            }
        }
        System.out.println("Nombre más largo: " + masLargo);
    }
}
El mes 10 es octubre
Escribe un mes: Agosto
agosto es el mes 8
Nombre más largo: septiembre

El array meses funciona como una tabla de traducción: meses[mes - 1] sustituye a un switch de 12 casos. Fíjate en el - 1: los meses empiezan en 1 y los índices en 0.


12. Ejemplos completos

12.1. Notas de una clase

Dos arrays paralelos (la posición i de cada uno corresponde al mismo alumno) y un método para cada tarea. Al ordenar, hay que mover a la vez la nota y el nombre:

public class NotasClase {

    static double media(double[] v) {
        double suma = 0;
        for (double x : v) {
            suma += x;
        }
        return suma / v.length;
    }

    static int posicionMaximo(double[] v) {
        int pos = 0;
        for (int i = 1; i < v.length; i++) {
            if (v[i] > v[pos]) {
                pos = i;
            }
        }
        return pos;
    }

    static int contarAprobados(double[] v) {
        int n = 0;
        for (double x : v) {
            if (x >= 5) {
                n++;
            }
        }
        return n;
    }

    // Ordena las notas de mayor a menor moviendo también los nombres
    static void ordenarPorNota(String[] nombres, double[] notas) {
        for (int i = 0; i < notas.length - 1; i++) {
            int posMax = i;
            for (int j = i + 1; j < notas.length; j++) {
                if (notas[j] > notas[posMax]) {
                    posMax = j;
                }
            }
            double auxNota = notas[i];
            notas[i] = notas[posMax];
            notas[posMax] = auxNota;
            String auxNombre = nombres[i];
            nombres[i] = nombres[posMax];
            nombres[posMax] = auxNombre;
        }
    }

    public static void main(String[] args) {
        // Arrays "paralelos": la posición i de ambos corresponde al mismo alumno
        String[] nombres = {"Ana", "Bruno", "Carla", "David", "Elena", "Fran"};
        double[] notas = {6.5, 4.0, 9.2, 7.8, 3.5, 8.1};

        System.out.printf("Media de la clase: %.2f%n", media(notas));
        int mejor = posicionMaximo(notas);
        System.out.println("Mejor nota: " + nombres[mejor] + " (" + notas[mejor] + ")");
        System.out.println("Aprobados: " + contarAprobados(notas) + " de " + notas.length);

        ordenarPorNota(nombres, notas);
        System.out.println("Clasificación:");
        for (int i = 0; i < notas.length; i++) {
            System.out.printf("%d. %-6s %4.1f %s%n", i + 1, nombres[i], notas[i],
                    notas[i] >= 5 ? "" : "(suspenso)");
        }
    }
}
Media de la clase: 6.52
Mejor nota: Carla (9.2)
Aprobados: 4 de 6
Clasificación:
1. Carla   9.2 
2. Fran    8.1 
3. David   7.8 
4. Ana     6.5 
5. Bruno   4.0 (suspenso)
6. Elena   3.5 (suspenso)

💡 Consejo: Los arrays paralelos son frágiles: si ordenas uno y te olvidas del otro, los datos se mezclan. En el módulo 10 aprenderás a crear una clase Alumno con nombre y nota, y usarás un único array de alumnos.

12.2. Tres en raya

Un tablero es una matriz de char. Cada jugada valida la casilla, la marca y comprueba si hay tres en línea:

import java.util.Scanner;

public class TresEnRaya {

    static void mostrar(char[][] t) {
        System.out.println("  0 1 2");
        for (int f = 0; f < 3; f++) {
            System.out.print(f + " ");
            for (int c = 0; c < 3; c++) {
                System.out.print(t[f][c] + " ");
            }
            System.out.println();
        }
    }

    static boolean gana(char[][] t, char j) {
        for (int i = 0; i < 3; i++) {
            if (t[i][0] == j && t[i][1] == j && t[i][2] == j) return true;   // fila i
            if (t[0][i] == j && t[1][i] == j && t[2][i] == j) return true;   // columna i
        }
        return (t[0][0] == j && t[1][1] == j && t[2][2] == j)               // diagonales
                || (t[0][2] == j && t[1][1] == j && t[2][0] == j);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        char[][] tablero = new char[3][3];
        for (char[] fila : tablero) {
            java.util.Arrays.fill(fila, '.');
        }

        char jugador = 'X';
        boolean terminado = false;
        int jugadas = 0;
        while (!terminado) {
            mostrar(tablero);
            System.out.print("Juega " + jugador + " (fila columna): ");
            int f = sc.nextInt();
            int c = sc.nextInt();
            if (f < 0 || f > 2 || c < 0 || c > 2 || tablero[f][c] != '.') {
                System.out.println("Casilla no válida.");
                continue;
            }
            tablero[f][c] = jugador;
            jugadas++;
            if (gana(tablero, jugador)) {
                mostrar(tablero);
                System.out.println("¡Gana " + jugador + "!");
                terminado = true;
            } else if (jugadas == 9) {
                mostrar(tablero);
                System.out.println("Empate.");
                terminado = true;
            } else {
                jugador = (jugador == 'X') ? 'O' : 'X';
            }
        }
    }
}

Final de una partida real (antes, el jugador X intentó repetir la casilla 1 1 y el programa respondió «Casilla no válida.»):

...
2 . . . 
Juega O (fila columna): 2 2
  0 1 2
0 O . X 
1 . X . 
2 . . O 
Juega X (fila columna): 2 0
  0 1 2
0 O . X 
1 . X . 
2 X . O 
¡Gana X!

12.3. Estadísticas

Media, mediana, moda, desviación típica y rango de un conjunto de edades. La moda usa un array de frecuencias, una técnica muy potente: el propio valor (la edad) se usa como índice del contador.

import java.util.Arrays;

public class Estadisticas {
    public static void main(String[] args) {
        int[] edades = {19, 21, 18, 22, 19, 25, 20, 19, 23, 21};

        // Media
        double suma = 0;
        for (int e : edades) {
            suma += e;
        }
        double media = suma / edades.length;

        // Mediana: valor central del array ORDENADO (trabajamos sobre una copia)
        int[] ord = Arrays.copyOf(edades, edades.length);
        Arrays.sort(ord);
        int n = ord.length;
        double mediana = (n % 2 == 1) ? ord[n / 2] : (ord[n / 2 - 1] + ord[n / 2]) / 2.0;

        // Moda: tabla de frecuencias indexada por la propia edad
        int[] frecuencia = new int[ord[n - 1] + 1];   // índices de 0 a la edad máxima
        for (int e : edades) {
            frecuencia[e]++;
        }
        int moda = 0;
        for (int edad = 0; edad < frecuencia.length; edad++) {
            if (frecuencia[edad] > frecuencia[moda]) {
                moda = edad;
            }
        }

        // Desviación típica
        double sumaCuadrados = 0;
        for (int e : edades) {
            sumaCuadrados += (e - media) * (e - media);
        }
        double desviacion = Math.sqrt(sumaCuadrados / n);

        System.out.println("Datos ordenados: " + Arrays.toString(ord));
        System.out.printf("Media: %.2f%n", media);
        System.out.println("Mediana: " + mediana);
        System.out.println("Moda: " + moda + " (" + frecuencia[moda] + " veces)");
        System.out.printf("Desviación típica: %.2f%n", desviacion);
        System.out.println("Rango: " + ord[0] + " - " + ord[n - 1]);
    }
}
Datos ordenados: [18, 19, 19, 19, 20, 21, 21, 22, 23, 25]
Media: 20.70
Mediana: 20.5
Moda: 19 (3 veces)
Desviación típica: 2.05
Rango: 18 - 25

La mediana se calcula sobre una copia ordenada para no alterar el orden original de los datos, que quizá haga falta después.


13. Ejercicios

Los seis primeros están resueltos; intenta hacerlos antes de mirar la solución.

Ejercicio 1: ¿Array capicúa?

Lee n números en un array, muéstralos al revés y di si el array es capicúa (se lee igual en los dos sentidos).

Ver solución
import java.util.Arrays;
import java.util.Scanner;

public class Capicua {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("¿Cuántos números? ");
        int[] v = new int[sc.nextInt()];
        System.out.print("Introdúcelos: ");
        for (int i = 0; i < v.length; i++) {
            v[i] = sc.nextInt();
        }

        boolean capicua = true;
        for (int i = 0, j = v.length - 1; i < j && capicua; i++, j--) {
            if (v[i] != v[j]) {
                capicua = false;
            }
        }

        System.out.print("Al revés: [");
        for (int i = v.length - 1; i >= 0; i--) {
            System.out.print(v[i] + (i > 0 ? ", " : ""));
        }
        System.out.println("]");
        System.out.println(Arrays.toString(v) + (capicua ? " es capicúa" : " no es capicúa"));
    }
}
¿Cuántos números? 5
Introdúcelos: 3 8 1 8 3
Al revés: [3, 8, 1, 8, 3]
[3, 8, 1, 8, 3] es capicúa

Para comprobar si es capicúa no hace falta crear el array invertido: basta con comparar v[i] con v[j] mientras los índices se acercan. En cuanto falla una pareja, la bandera detiene el bucle.

Ejercicio 2: Eliminar duplicados

Escribe un método que reciba un array de enteros y devuelva otro con los mismos valores pero sin repeticiones, manteniendo el orden de la primera aparición.

Ver solución
import java.util.Arrays;

public class SinDuplicados {

    static int[] sinDuplicados(int[] v) {
        int[] resultado = new int[v.length];     // como mucho, todos distintos
        int n = 0;                               // cuántos llevamos guardados
        for (int x : v) {
            boolean repetido = false;
            for (int i = 0; i < n && !repetido; i++) {
                if (resultado[i] == x) {
                    repetido = true;
                }
            }
            if (!repetido) {
                resultado[n] = x;
                n++;
            }
        }
        return Arrays.copyOf(resultado, n);      // recortamos al tamaño real
    }

    public static void main(String[] args) {
        int[] v = {4, 7, 4, 1, 9, 7, 7, 2, 1};
        System.out.println(Arrays.toString(v) + " -> " + Arrays.toString(sinDuplicados(v)));
    }
}
[4, 7, 4, 1, 9, 7, 7, 2, 1] -> [4, 7, 1, 9, 2]

Como no sabemos de antemano cuántos valores distintos habrá, creamos un array del tamaño máximo posible, llevamos la cuenta de los usados en n y al final lo recortamos con Arrays.copyOf. Es una técnica muy común con arrays, cuyo tamaño no puede cambiar. (Con las listas del módulo 16 será más sencillo.)

Ejercicio 3: Mezclar dos arrays ordenados

Escribe un método que reciba dos arrays ordenados y devuelva un tercero, también ordenado, con todos los elementos de ambos. No vale juntarlos y llamar a Arrays.sort.

Ver solución
import java.util.Arrays;

public class Mezclar {

    // Mezcla dos arrays ORDENADOS en uno nuevo también ordenado
    static int[] mezclar(int[] a, int[] b) {
        int[] r = new int[a.length + b.length];
        int i = 0, j = 0, k = 0;
        while (i < a.length && j < b.length) {
            if (a[i] <= b[j]) {
                r[k++] = a[i++];       // usa el valor y después incrementa
            } else {
                r[k++] = b[j++];
            }
        }
        while (i < a.length) {         // lo que quede de a
            r[k++] = a[i++];
        }
        while (j < b.length) {         // lo que quede de b
            r[k++] = b[j++];
        }
        return r;
    }

    public static void main(String[] args) {
        int[] a = {1, 4, 9, 12};
        int[] b = {2, 3, 10, 15, 20};
        System.out.println(Arrays.toString(mezclar(a, b)));
    }
}
[1, 2, 3, 4, 9, 10, 12, 15, 20]

Tres índices: uno por array de entrada y otro para el resultado. En cada paso se copia el menor de los dos candidatos. Al agotarse uno de los arrays, se copia lo que queda del otro. r[k++] = a[i++] usa los valores actuales de k e i y después los incrementa (módulo 03). Esta mezcla es el corazón del algoritmo mergesort y recorre cada elemento una sola vez: O(n).

Ejercicio 4: Producto de matrices

Escribe un método que multiplique una matriz f × k por otra k × c. Pruébalo con una de 2 × 3 y otra de 3 × 2.

Ver solución
public class ProductoMatrices {

    static int[][] multiplicar(int[][] a, int[][] b) {
        int filas = a.length, columnas = b[0].length, comun = b.length;
        int[][] r = new int[filas][columnas];
        for (int f = 0; f < filas; f++) {
            for (int c = 0; c < columnas; c++) {
                for (int k = 0; k < comun; k++) {   // fila f de a por columna c de b
                    r[f][c] += a[f][k] * b[k][c];
                }
            }
        }
        return r;
    }

    public static void main(String[] args) {
        int[][] a = {{1, 2, 3}, {4, 5, 6}};          // 2x3
        int[][] b = {{7, 8}, {9, 10}, {11, 12}};     // 3x2
        int[][] r = multiplicar(a, b);               // 2x2
        for (int[] fila : r) {
            System.out.println(java.util.Arrays.toString(fila));
        }
    }
}
[58, 64]
[139, 154]

Cada elemento r[f][c] es la suma de los productos de la fila f de a por la columna c de b: por ejemplo, r[0][0] = 1·7 + 2·9 + 3·11 = 58. Hacen falta tres bucles anidados. Para que el producto sea posible, el número de columnas de a debe coincidir con el de filas de b.

Ejercicio 5: Recuento de votos

Una encuesta pide puntuar un producto de 1 a 5. Dado el array de votos, cuenta cuántos hay de cada puntuación y dibuja un histograma con asteriscos, de la puntuación más alta a la más baja.

Ver solución
public class Votos {
    public static void main(String[] args) {
        int[] votos = {3, 5, 4, 3, 1, 5, 5, 2, 4, 5, 3, 4, 5, 1, 4, 5};
        int[] conteo = new int[6];           // índices 1..5 (el 0 no se usa)

        for (int v : votos) {
            conteo[v]++;                     // el voto es el índice
        }

        for (int estrellas = 5; estrellas >= 1; estrellas--) {
            System.out.printf("Puntuación %d | %-8s %d%n", estrellas,
                    "*".repeat(conteo[estrellas]), conteo[estrellas]);
        }
    }
}
Puntuación 5 | ******   6
Puntuación 4 | ****     4
Puntuación 3 | ***      3
Puntuación 2 | *        1
Puntuación 1 | **       2

conteo[v]++ es el array de frecuencias de la sección 12.3: el voto se usa directamente como índice, sin ningún if ni switch. Creamos el array con 6 posiciones para que los índices 1 a 5 coincidan con las puntuaciones, aunque la 0 quede sin usar.

Ejercicio 6: Cuadrado mágico

Un cuadrado mágico es una matriz cuadrada en la que todas las filas, todas las columnas y las dos diagonales suman lo mismo. Escribe un método boolean esMagico(int[][] m) y pruébalo.

Ver solución
public class CuadradoMagico {

    static boolean esMagico(int[][] m) {
        int n = m.length;
        int objetivo = 0;
        for (int c = 0; c < n; c++) {
            objetivo += m[0][c];             // la suma de la primera fila es la referencia
        }
        int diag1 = 0, diag2 = 0;
        for (int i = 0; i < n; i++) {
            int sumaFila = 0, sumaCol = 0;
            for (int j = 0; j < n; j++) {
                sumaFila += m[i][j];
                sumaCol += m[j][i];
            }
            if (sumaFila != objetivo || sumaCol != objetivo) {
                return false;
            }
            diag1 += m[i][i];
            diag2 += m[i][n - 1 - i];
        }
        return diag1 == objetivo && diag2 == objetivo;
    }

    public static void main(String[] args) {
        int[][] lo = {{2, 7, 6}, {9, 5, 1}, {4, 3, 8}};
        int[][] durero = {{16, 3, 2, 13}, {5, 10, 11, 8}, {9, 6, 7, 12}, {4, 15, 14, 1}};
        int[][] normal = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        System.out.println("Lo Shu: " + esMagico(lo));
        System.out.println("Durero: " + esMagico(durero));
        System.out.println("1..9 en orden: " + esMagico(normal));
    }
}
Lo Shu: true
Durero: true
1..9 en orden: false

La suma de la primera fila sirve de referencia. En un mismo bucle se calculan la suma de la fila i (m[i][j]) y la de la columna i (m[j][i], con los índices cambiados), y en cuanto una no coincide se devuelve false. El cuadrado Lo Shu es un cuadrado mágico chino muy antiguo, y el de 4 × 4 aparece en el grabado Melancolía I de Durero (1514).

Ejercicio 7: El segundo mayor (propuesto)

Escribe un método que devuelva el segundo valor más grande de un array recorriéndolo una sola vez y sin ordenarlo. Piensa qué debe devolver si todos los valores son iguales o si el array tiene un solo elemento.

Ejercicio 8: Filtrar pares (propuesto)

Escribe un método int[] pares(int[] v) que devuelva un array nuevo, del tamaño exacto, con los números pares de v. Hazlo de dos formas: contando primero cuántos pares hay para crear el array justo, y con la técnica del ejercicio 2.

Ejercicio 9: Ordenar alumnos por nombre (propuesto)

Modifica NotasClase para ordenar los alumnos por nombre con el algoritmo de inserción, manteniendo la correspondencia con las notas. Para comparar dos String alfabéticamente usa a.compareTo(b) > 0 (verás compareTo en el módulo 09).

Ejercicio 10: Buscaminas (propuesto)

Dada una matriz de boolean en la que true indica una mina, genera una matriz de int en la que cada casilla sin mina contenga el número de minas que hay en las 8 casillas vecinas (y −1 en las casillas con mina). Ten cuidado con los bordes: no te salgas de la matriz.

Ejercicio 11: Matriz en espiral (propuesto)

Rellena una matriz de n × n con los números de 1 a n² siguiendo una espiral en el sentido de las agujas del reloj, empezando por la esquina superior izquierda, y muéstrala. Para n = 4, la primera fila es 1 2 3 4 y la segunda, 12 13 14 5.

Ejercicio 12: Comparar algoritmos de ordenación (propuesto)

Crea tres copias de un mismo array de 2000 números aleatorios y ordénalas con burbuja, selección e inserción. Añade a cada método un contador de comparaciones y otro de intercambios o desplazamientos y muestra una tabla con los resultados. Repite la prueba con un array ya ordenado y con uno ordenado al revés. ¿Coinciden los resultados con la tabla de la sección 9.4?


Resumen

Concepto Sintaxis / idea clave
Declarar y crear int[] v = new int[5]; o int[] v = {1, 2, 3};
Índices De 0 a v.length - 1; fuera de rango → ArrayIndexOutOfBoundsException
Longitud v.length (atributo, sin paréntesis)
Recorrer for (int i = 0; i < v.length; i++) o for-each for (int x : v)
Referencias b = a no copia; a == b compara referencias
Copiar clone(), Arrays.copyOf, Arrays.copyOfRange, System.arraycopy
Clase Arrays toString, sort, fill, equals, binarySearch, deepToString
Métodos Un método puede modificar el array recibido y devolver arrays nuevos
Algoritmos básicos Suma, media, máximo/mínimo (por posición), contar, invertir, rotar
Búsqueda lineal O(n); cualquier array
Búsqueda binaria O(log n); solo arrays ordenados
Burbuja, selección, inserción O(n²); quicksort y mergesort, O(n log n)
Matrices int[][] m = new int[f][c];, m[i][j], m.length, m[i].length
Matrices irregulares new int[f][] y después cada fila con su tamaño
Arrays de String Valores por defecto null; comparar con equals