Ordenación por selección
En cada pasada busca el menor de la parte sin ordenar y lo intercambia con el primero de esa parte. Siempre O(n²) comparaciones, pero como mucho n − 1 intercambios. No es estable.
Visualízalo paso a paso
Cambia los datos, dale a reproducir y sigue cada paso en el dibujo, en la línea de Java que se ejecuta y en sus variables.
Ordenación por selección
Escribe los números y mira cómo en cada pasada se busca el menor y se pone en su sitio.
- en la zona de trabajo
Paso 1
Array inicial: [29, 10, 14, 37, 13, 5].
1static void seleccion(int[] a) {
2 for (int i = 0; i < a.length - 1; i++) { // comparaciones = 0, intercambios = 0
3 int min = i;
4 for (int j = i + 1; j < a.length; j++) {
5 if (a[j] < a[min]) {
6 min = j;
7 }
8 }
9 int t = a[i]; a[i] = a[min]; a[min] = t;
10 }
11}Variables
- comparaciones
- 0
- intercambios
- 0
Atajos con el foco dentro del visualizador: ← → paso a paso, Espacio reproducir o pausar, Inicio/Fin ir al principio o al final.
La idea
La ordenación por selección hace lo que haría cualquiera a mano: busca el menor de todos y lo pone el primero; luego busca el menor de los que quedan y lo pone el segundo; y así hasta el final.
El array queda dividido en dos partes: a la izquierda, la parte ya ordenada (que crece una posición en cada pasada) y a la derecha, la que falta. Cada pasada recorre toda la parte de la derecha para encontrar su mínimo y lo intercambia con el primero de esa parte.
Su gran ventaja es que hace muy pocos movimientos: exactamente un intercambio por pasada, n − 1 en total, esté como esté el array. Su gran inconveniente es que no aprovecha nada: aunque el array ya esté ordenado, sigue recorriendo la parte derecha entera para comprobar cuál es el menor, así que siempre hace n(n − 1)/2 comparaciones.
No es estable: el intercambio puede mandar un elemento muy lejos y saltarse a otro igual que él. Por eso no sirve para ordenar por un segundo criterio respetando el orden anterior.
Cuándo usarlo
- Cuando escribir o mover datos es mucho más caro que compararlos (memoria flash, elementos grandes que no se pueden mover por referencia): hace como mucho n − 1 intercambios.
- Cuando solo hacen falta los k menores: bastan k pasadas, sin ordenar el resto.
- Para aprender: es el más intuitivo y su número de comparaciones no depende de los datos, lo que facilita calcularlo.
Cuándo no
- Con muchos datos: siempre es O(n²), incluso si el array ya estaba ordenado (la burbuja y la inserción, en ese caso, son O(n)).
- Cuando se necesita estabilidad (ordenar por apellido respetando un orden previo por nombre).
Paso a paso
- Parte ordenada y sin ordenar. La posición
isepara las dos partes:a[0..i-1]ya está ordenada y en su sitio definitivo;a[i..n-1]falta. - Buscar el mínimo. Se recorre
a[i..n-1]guardando la posición del menor visto (min): empieza eniy cambia cada vez que aparece uno más pequeño. - Intercambiar. Se intercambia
a[i]cona[min]: el menor de la parte derecha pasa a ser el último de la parte ordenada. - Avanzar. Se repite con
i + 1hastai = n − 2: el último elemento queda solo y, por fuerza, es el mayor.
El código
Selección pasada a pasada
Cada pasada fija una posición más por la izquierda.
1import java.util.Arrays;
2
3public class Main {
4 /** Ordena a por selección: en cada pasada busca el menor de la parte sin ordenar
5 y lo intercambia con el primero de esa parte. */
6 static void seleccion(int[] a) {
7 for (int i = 0; i < a.length - 1; i++) {
8 int min = i; // posición del menor visto hasta ahora
9 for (int j = i + 1; j < a.length; j++)
10 if (a[j] < a[min]) min = j;
11 int t = a[i]; a[i] = a[min]; a[min] = t; // un solo intercambio por pasada
12 System.out.println("Pasada " + (i + 1) + ": el menor es " + a[i] + " → " + Arrays.toString(a));
13 }
14 }
15
16 public static void main(String[] args) {
17 int[] a = {29, 10, 14, 37, 13, 5};
18 seleccion(a);
19 }
20}def seleccion(a):
"""Ordena a por selección: en cada pasada busca el menor de la parte sin ordenar
y lo intercambia con el primero de esa parte."""
for i in range(len(a) - 1):
minimo = i # posición del menor visto hasta ahora
for j in range(i + 1, len(a)):
if a[j] < a[minimo]:
minimo = j
a[i], a[minimo] = a[minimo], a[i] # un solo intercambio por pasada
print(f"Pasada {i + 1}: el menor es {a[i]} → {a}")
seleccion([29, 10, 14, 37, 13, 5])/** Ordena a por selección: en cada pasada busca el menor de la parte sin ordenar
y lo intercambia con el primero de esa parte. */
function seleccion(a) {
for (let i = 0; i < a.length - 1; i++) {
let min = i; // posición del menor visto hasta ahora
for (let j = i + 1; j < a.length; j++)
if (a[j] < a[min]) min = j;
[a[i], a[min]] = [a[min], a[i]]; // un solo intercambio por pasada
console.log(`Pasada ${i + 1}: el menor es ${a[i]} → [${a.join(", ")}]`);
}
}
seleccion([29, 10, 14, 37, 13, 5]);using System;
class Program {
// Ordena a por selección: en cada pasada busca el menor de la parte sin ordenar
// y lo intercambia con el primero de esa parte.
static void Seleccion(int[] a) {
for (int i = 0; i < a.Length - 1; i++) {
int min = i; // posición del menor visto hasta ahora
for (int j = i + 1; j < a.Length; j++)
if (a[j] < a[min]) min = j;
(a[i], a[min]) = (a[min], a[i]); // un solo intercambio por pasada
Console.WriteLine(quot;Pasada {i + 1}: el menor es {a[i]} → [{string.Join(", ", a)}]");
}
}
static void Main() {
Seleccion(new[] { 29, 10, 14, 37, 13, 5 });
}
}<?php
/** Ordena $a por selección: en cada pasada busca el menor de la parte sin ordenar
y lo intercambia con el primero de esa parte. */
function seleccion(array &$a): void {
$n = count($a);
for ($i = 0; $i < $n - 1; $i++) {
$min = $i; // posición del menor visto hasta ahora
for ($j = $i + 1; $j < $n; $j++)
if ($a[$j] < $a[$min]) $min = $j;
[$a[$i], $a[$min]] = [$a[$min], $a[$i]]; // un solo intercambio por pasada
echo "Pasada " . ($i + 1) . ": el menor es {$a[$i]} → [" . implode(", ", $a) . "]\n";
}
}
$a = [29, 10, 14, 37, 13, 5];
seleccion($a);Salida al ejecutarlo (la misma en los 5 lenguajes)
Pasada 1: el menor es 5 → [5, 10, 14, 37, 13, 29] Pasada 2: el menor es 10 → [5, 10, 14, 37, 13, 29] Pasada 3: el menor es 13 → [5, 10, 13, 37, 14, 29] Pasada 4: el menor es 14 → [5, 10, 13, 14, 37, 29] Pasada 5: el menor es 29 → [5, 10, 13, 14, 29, 37]
Por qué no es estable
Dos cartas con el mismo valor cambian de orden: el intercambio de la primera pasada manda el 5♥ al final, por detrás del 5♠.
1import java.util.Arrays;
2
3public class Main {
4 record Carta(int valor, String palo) {
5 @Override public String toString() { return valor + palo; }
6 }
7
8 public static void main(String[] args) {
9 Carta[] c = { new Carta(5, "♥"), new Carta(5, "♠"), new Carta(2, "♦") };
10 // Selección por valor: el 2♦ se intercambia con el primer 5 y lo manda detrás del otro 5
11 for (int i = 0; i < c.length - 1; i++) {
12 int min = i;
13 for (int j = i + 1; j < c.length; j++)
14 if (c[j].valor() < c[min].valor()) min = j;
15 Carta t = c[i]; c[i] = c[min]; c[min] = t;
16 }
17 System.out.println(Arrays.toString(c) + ": el 5♥ iba antes que el 5♠ y ahora va despu és");
18 }
19}from dataclasses import dataclass
@dataclass
class Carta:
valor: int
palo: str
def __str__(self):
return f"{self.valor}{self.palo}"
c = [Carta(5, "♥"), Carta(5, "♠"), Carta(2, "♦")]
# Selección por valor: el 2♦ se intercambia con el primer 5 y lo manda detrás del otro 5
for i in range(len(c) - 1):
minimo = i
for j in range(i + 1, len(c)):
if c[j].valor < c[minimo].valor:
minimo = j
c[i], c[minimo] = c[minimo], c[i]
print("[" + ", ".join(map(str, c)) + "]: el 5♥ iba antes que el 5♠ y ahora va después")const c = [{ valor: 5, palo: "♥" }, { valor: 5, palo: "♠" }, { valor: 2, palo: "♦" }];
// Selección por valor: el 2♦ se intercambia con el primer 5 y lo manda detrás del otro 5
for (let i = 0; i < c.length - 1; i++) {
let min = i;
for (let j = i + 1; j < c.length; j++)
if (c[j].valor < c[min].valor) min = j;
[c[i], c[min]] = [c[min], c[i]];
}
console.log("[" + c.map((x) => x.valor + x.palo).join(", ") + "]: el 5♥ iba antes que el 5♠ y ahora va después");using System;
using System.Linq;
record Carta(int Valor, string Palo) {
public override string ToString() => quot;{Valor}{Palo}";
}
class Program {
static void Main() {
Carta[] c = { new(5, "♥"), new(5, "♠"), new(2, "♦") };
// Selección por valor: el 2♦ se intercambia con el primer 5 y lo manda detrás del otro 5
for (int i = 0; i < c.Length - 1; i++) {
int min = i;
for (int j = i + 1; j < c.Length; j++)
if (c[j].Valor < c[min].Valor) min = j;
(c[i], c[min]) = (c[min], c[i]);
}
Console.WriteLine("[" + string.Join(", ", c.Select(x => x.ToString())) + "]: el 5♥ iba antes que el 5♠ y ahora va después");
}
}<?php
$c = [[5, "♥"], [5, "♠"], [2, "♦"]];
// Selección por valor: el 2♦ se intercambia con el primer 5 y lo manda detrás del otro 5
$n = count($c);
for ($i = 0; $i < $n - 1; $i++) {
$min = $i;
for ($j = $i + 1; $j < $n; $j++)
if ($c[$j][0] < $c[$min][0]) $min = $j;
[$c[$i], $c[$min]] = [$c[$min], $c[$i]];
}
echo "[" . implode(", ", array_map(fn($x) => $x[0] . $x[1], $c)) . "]: el 5♥ iba antes que el 5♠ y ahora va después\n";Salida al ejecutarlo (la misma en los 5 lenguajes)
[2♦, 5♠, 5♥]: el 5♥ iba antes que el 5♠ y ahora va después
Traza: selección de {29, 10, 14, 37, 13, 5}
| Pasada | Menor de la parte derecha | Intercambio | Array |
|---|---|---|---|
| 1 | 5 (posición 5) | a[0] ↔ a[5] | 5 10 14 37 13 29 |
| 2 | 10 (posición 1) | ninguno | 5 10 14 37 13 29 |
| 3 | 13 (posición 4) | a[2] ↔ a[4] | 5 10 13 37 14 29 |
| 4 | 14 (posición 4) | a[3] ↔ a[4] | 5 10 13 14 37 29 |
| 5 | 29 (posición 5) | a[4] ↔ a[5] | 5 10 13 14 29 37 |
Cinco pasadas para seis elementos: siempre n − 1, aunque en la última el menor ya estuviera en su sitio.
Complejidad
| Caso | Comparaciones | Intercambios | Coste |
|---|---|---|---|
| Mejor (ya ordenado) | n(n − 1)/2 | n − 1 (cada uno consigo mismo) | O(n²) |
| Medio | n(n − 1)/2 | ≈ n − 1 | O(n²) |
| Peor | n(n − 1)/2 | n − 1 | O(n²) |
Las comparaciones no dependen de los datos. Memoria extra: O(1). Comparada con la burbuja hace los mismos ≈ n²/2 de comparaciones, pero muchísimos menos intercambios.
- Mejor caso: O(n²)
- Caso medio: O(n²)
- Peor caso: O(n²)
Siempre hace las mismas n(n − 1)/2 comparaciones, esté como esté el array; a cambio, como mucho n − 1 intercambios. Las curvas grises son las demás clases, para comparar.
En la práctica
- La idea de «buscar el mínimo de lo que queda» es la base del heapsort, que hace lo mismo pero encuentra cada mínimo (o máximo) en O(log n) gracias a un montículo.
- Elegir los k mejores (un podio, los 10 productos más vendidos) con k pasadas de selección es O(k·n): para k pequeño es más rápido que ordenarlo todo.
- Se usa en sistemas donde escribir cuesta mucho más que leer, como algunas memorias EEPROM o flash con ciclos de escritura limitados.
Errores típicos
- Guardar el valor mínimo en vez de su posición: al final no se sabe con quién intercambiar.
- Empezar el bucle interno en 0 en lugar de en
i + 1: vuelve a mirar la parte ya ordenada y estropea el resultado. - Intercambiar dentro del bucle interno cada vez que aparece uno menor: sigue ordenando, pero hace muchos más intercambios y pierde su única ventaja.
- Esperar que sea estable: el intercambio puede saltarse elementos iguales.
- Hacer la última pasada (
i = n − 1): no hace daño, pero sobra, porque un solo elemento ya está ordenado.
Ejercicios
Cada ejercicio se corrige solo con sus pruebas (algunas ocultas). Escribe tu solución en el editor y pulsa Ejecutar o Comprobar; la solución explicada está debajo, por si te atascas.
1. El podio sin ordenarlo todo
La primera línea es k, cuántos corredores hay que premiar. Cada línea siguiente es un corredor: su nombre y su tiempo mm:ss. Muestra los k más rápidos, en orden, usando solo k pasadas de selección (el resto no hace falta ordenarlo), y cuántas comparaciones has hecho frente a las que haría la selección completa. Completa seleccionParcial.
- Entrada:
3, luego líneas comoAna 12:31(minutos de 1 a 3 cifras y segundos de 00 a 59). - Salida:
1. Marta 11:40,2. …y al finalComparaciones: C (ordenarlo todo con selección: T). - Errores:
k no válido: «texto»(k es un entero de 1 a 999),Tiempo no válido: «línea»(se salta) yNo hay corredores.
Ejemplo
3 Ana 12:31 Luis 11:58 Eva 13:02 Marta 11:40 Pablo 12:05 Sara 14:10
1. Marta 11:40 2. Luis 11:58 3. Pablo 12:05 Comparaciones: 12 (ordenarlo todo con selección: 15)
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 record Corredor(String nombre, int segundos) { }
5
6 /** Deja en las k primeras posiciones los k menores tiempos, ya ordenados, haciendo solo
7 k pasadas de selección (no hace falta ordenar el resto). Devuelve las comparaciones. */
8 static long seleccionParcial(List<Corredor> l, int k) {
9 long comparaciones = 0;
10 for (int i = 0; i < Math.min(k, l.size() - 1); i++) {
11 int min = i;
12 for (int j = i + 1; j < l.size(); j++) {
13 comparaciones++;
14 if (l.get(j).segundos() < l.get(min).segundos()) min = j;
15 }
16 Corredor t = l.get(i);
17 l.set(i, l.get(min));
18 l.set(min, t);
19 }
20 return comparaciones;
21 }
22
23 public static void main(String[] args) {
24 Scanner sc = new Scanner(System.in);
25 String primera = sc.hasNextLine() ? sc.nextLine().trim() : "";
26 if (!primera.matches("[1-9]\\d{0,2}")) {
27 System.out.println("k no válido: «" + primera + "»");
28 return;
29 }
30 int k = Integer.parseInt(primera);
31 List<Corredor> l = new ArrayList<>();
32 while (sc.hasNextLine()) {
33 String linea = sc.nextLine().trim();
34 if (linea.isEmpty()) continue;
35 String[] p = linea.split("\\s+");
36 if (p.length != 2 || !p[1].matches("\\d{1,3}:[0-5]\\d")) {
37 System.out.println("Tiempo no válido: «" + linea + "»");
38 continue;
39 }
40 String[] t = p[1].split(":");
41 l.add(new Corredor(p[0], Integer.parseInt(t[0]) * 60 + Integer.parseInt(t[1])));
42 }
43 if (l.isEmpty()) {
44 System.out.println("No hay corredores");
45 return;
46 }
47 long c = seleccionParcial(l, k);
48 for (int i = 0; i < Math.min(k, l.size()); i++)
49 System.out.printf("%d. %s %d:%02d%n", i + 1, l.get(i).nombre(), l.get(i).segundos() / 60, l.get(i).segundos() % 60);
50 long todo = (long) l.size() * (l.size() - 1) / 2;
51 System.out.println("Comparaciones: " + c + " (ordenarlo todo con selección: " + todo + ")");
52 }
53}Cada pasada de la selección fija definitivamente una posición por la izquierda, así que tras k pasadas las k primeras ya contienen los k menores, en orden, aunque el resto siga desordenado.
Con n corredores y k pequeño son unas k·n comparaciones en vez de n²/2: para un podio de 3 entre 10.000, unas 30.000 en lugar de 50 millones.
2. Selección por los dos extremos
Mejora la selección buscando en cada pasada el menor y el mayor a la vez: el menor va al principio de la zona y el mayor al final, así que cada pasada fija dos posiciones y hacen falta la mitad de pasadas. Cuidado con un caso: si el mayor estaba justo en la posición donde se pone el menor, el primer intercambio lo ha movido. Completa pasada.
- Entrada: una línea con números enteros separados por espacios.
- Por cada pasada:
Pasada 1: 1 al principio y 9 al final → 1 5 3 7 2 9; al final,Ordenado en P pasadas: …. - Errores:
No hay númerosyNúmero no válido: «x».
Ejemplo
3 9 5 1 7 2
Pasada 1: 1 al principio y 9 al final → 1 2 5 3 7 9 Pasada 2: 2 al principio y 7 al final → 1 2 5 3 7 9 Pasada 3: 3 al principio y 5 al final → 1 2 3 5 7 9 Ordenado en 3 pasadas: 1 2 3 5 7 9
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 static void cambiar(int[] a, int i, int j) {
5 int t = a[i]; a[i] = a[j]; a[j] = t;
6 }
7
8 /** Una pasada de selección por los dos extremos sobre a[ini..fin]: busca a la vez el menor y el
9 mayor, pone el menor en ini y el mayor en fin. */
10 static void pasada(int[] a, int ini, int fin) {
11 int min = ini, max = ini;
12 for (int j = ini + 1; j <= fin; j++) {
13 if (a[j] < a[min]) min = j;
14 if (a[j] > a[max]) max = j;
15 }
16 cambiar(a, ini, min);
17 if (max == ini) max = min; // el mayor estaba en ini y el intercambio lo ha llevado a min
18 cambiar(a, fin, max);
19 }
20
21 static String texto(int[] a) {
22 StringJoiner sj = new StringJoiner(" ");
23 for (int x : a) sj.add(String.valueOf(x));
24 return sj.toString();
25 }
26
27 public static void main(String[] args) {
28 Scanner sc = new Scanner(System.in);
29 String linea = sc.hasNextLine() ? sc.nextLine().trim() : "";
30 if (linea.isEmpty()) {
31 System.out.println("No hay números");
32 return;
33 }
34 String[] partes = linea.split("\\s+");
35 int[] a = new int[partes.length];
36 for (int i = 0; i < partes.length; i++) {
37 try {
38 a[i] = Integer.parseInt(partes[i]);
39 } catch (NumberFormatException e) {
40 System.out.println("Número no válido: «" + partes[i] + "»");
41 return;
42 }
43 }
44 int pasadas = 0;
45 for (int ini = 0, fin = a.length - 1; ini < fin; ini++, fin--) {
46 pasada(a, ini, fin);
47 pasadas++;
48 System.out.println("Pasada " + pasadas + ": " + a[ini] + " al principio y " + a[fin] + " al final → " + texto(a));
49 }
50 System.out.println("Ordenado en " + pasadas + (pasadas == 1 ? " pasada: " : " pasadas: ") + texto(a));
51 }
52}Cada pasada hace casi las mismas comparaciones que antes (dos por elemento), pero fija dos posiciones: el número total de comparaciones es parecido, el de pasadas se reduce a la mitad.
El caso del mayor en ini es el típico error de esta variante: sin la corrección, el segundo intercambio mueve el elemento que acaba de llegar a min en vez del mayor.
Test
Test: Ordenación por selección
0/5 respondidas · 0 aciertosElige una respuesta en cada pregunta: verás al momento si es correcta y por qué. Con un 80 % de aciertos se da por superada.
1.¿Cuántos intercambios hace como mucho la ordenación por selección con n elementos?
2.¿Cuántas comparaciones hace la selección sobre un array ya ordenado de 100 elementos?
3.Tras la pasada i de la selección, ¿qué parte del array está en su sitio definitivo?
4.¿Es estable la ordenación por selección?
5.¿En qué situación tiene ventaja sobre la burbuja?