Mergesort (ordenación por mezcla)
Parte el array por la mitad, ordena cada mitad con la misma idea y mezcla las dos mitades ordenadas en una. Siempre O(n log n) y estable, a cambio de un array auxiliar.
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.
Mergesort
Escribe los números y mira cómo se parten por la mitad y se mezclan ya ordenados.
- en la zona de trabajo
Paso 1
Array inicial: [38, 27, 43, 3, 9, 82, 10].
1static void mergeSort(int[] a, int ini, int fin) {
2 if (fin - ini < 2) return; // 0 o 1 elementos: ya ordenado
3 int mitad = (ini + fin) / 2; // comparaciones = 0, escrituras = 0
4 mergeSort(a, ini, mitad);
5 mergeSort(a, mitad, fin);
6 mezclar(a, ini, mitad, fin);
7}
8
9static void mezclar(int[] a, int ini, int mitad, int fin) {
10 int[] aux = Arrays.copyOfRange(a, ini, fin);
11 int i = 0, j = mitad - ini, k = ini;
12 while (i < mitad - ini && j < fin - ini) {
13 if (aux[i] <= aux[j]) a[k++] = aux[i++];
14 else a[k++] = aux[j++];
15 }
16 while (i < mitad - ini) a[k++] = aux[i++];
17 while (j < fin - ini) a[k++] = aux[j++];
18}Variables
- comparaciones
- 0
- escrituras
- 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
Mergesort se basa en una observación sencilla: mezclar dos listas que ya están ordenadas en una sola lista ordenada es fácil y rápido. Basta mirar el primer elemento de cada una, coger el menor y avanzar en esa lista; en una sola pasada están las dos mezcladas.
Así que, para ordenar un array, se parte por la mitad, se ordena cada mitad (con el mismo mergesort, recursivamente) y se mezclan. La recursividad para en los trozos de un elemento, que ya están ordenados. Es el ejemplo clásico de divide y vencerás.
Partir por la mitad una y otra vez da log₂ n niveles, y en cada nivel las mezclas recorren entre todas los n elementos: n log n en total, siempre, estén como estén los datos. Esa garantía es su gran ventaja frente a quicksort.
Su precio es la memoria: la mezcla necesita un array auxiliar del tamaño del tramo. Y su gran virtud práctica es que es estable si, ante un empate, la mezcla coge primero el de la izquierda (<=); por eso las bibliotecas lo usan (en forma de TimSort) para ordenar objetos.
Cuándo usarlo
- Cuando se necesita un O(n log n) garantizado, sin peor caso cuadrático.
- Cuando hace falta estabilidad: ordenar por varios criterios encadenando ordenaciones.
- Para listas enlazadas: se puede mezclar enganchando nodos, sin memoria extra y sin acceso directo.
- Para ordenar datos que no caben en memoria (ordenación externa): se ordenan trozos que caben y se mezclan los ficheros.
Cuándo no
- Si la memoria es muy justa: necesita O(n) de memoria extra en arrays.
- Con arrays pequeños: la recursividad y las copias pesan más que el trabajo; inserción es más rápida.
Paso a paso
- Caso base. Un tramo de 0 o 1 elementos ya está ordenado: no se hace nada.
- Dividir. Se calcula el medio y se parte el tramo
[desde, hasta)en[desde, medio)y[medio, hasta). - Ordenar las mitades. Se llama a mergesort con cada mitad. Al volver, cada mitad está ordenada.
- Mezclar. Con dos índices, uno por mitad, se va copiando a un array auxiliar el menor de los dos elementos señalados (
<=para que sea estable), y después lo que sobre de la mitad que no se ha acabado. Se copia el auxiliar de vuelta.
El código
Mergesort mostrando cada mezcla
Las mezclas salen en el orden en que las hace la recursividad: primero las de los trozos más pequeños de la izquierda.
1import java.util.Arrays;
2
3public class Main {
4 /** Ordena a[desde..hasta) y escribe cada mezcla que hace. */
5 static void mergesort(int[] a, int desde, int hasta) {
6 if (hasta - desde <= 1) return; // 0 o 1 elementos: ya está ordenado
7 int medio = (desde + hasta) / 2;
8 mergesort(a, desde, medio); // ordena la mitad izquierda
9 mergesort(a, medio, hasta); // y la derecha
10 String izq = Arrays.toString(Arrays.copyOfRange(a, desde, medio));
11 String der = Arrays.toString(Arrays.copyOfRange(a, medio, hasta));
12 mezclar(a, desde, medio, hasta);
13 System.out.println("mezclar " + izq + " y " + der + " → " + Arrays.toString(Arrays.copyOfRange(a, desde, hasta)));
14 }
15
16 /** Mezcla a[desde..medio) y a[medio..hasta), ya ordenados, en a[desde..hasta). */
17 static void mezclar(int[] a, int desde, int medio, int hasta) {
18 int[] tmp = new int[hasta - desde];
19 int i = desde, j = medio, k = 0;
20 while (i < medio && j < hasta) tmp[k++] = a[i] <= a[j] ? a[i++] : a[j++]; // <= : estable
21 while (i < medio) tmp[k++] = a[i++]; // lo que quede de la izquierda
22 while (j < hasta) tmp[k++] = a[j++]; // o de la derecha
23 System.arraycopy(tmp, 0, a, desde, tmp.length);
24 }
25
26 public static void main(String[] args) {
27 int[] a = {38, 27, 43, 3, 9, 82, 10};
28 mergesort(a, 0, a.length);
29 System.out.println("Resultado: " + Arrays.toString(a));
30 }
31}def mergesort(a, desde, hasta):
"""Ordena a[desde:hasta] y escribe cada mezcla que hace."""
if hasta - desde <= 1: # 0 o 1 elementos: ya está ordenado
return
medio = (desde + hasta) // 2
mergesort(a, desde, medio) # ordena la mitad izquierda
mergesort(a, medio, hasta) # y la derecha
izq, der = a[desde:medio], a[medio:hasta]
mezclar(a, desde, medio, hasta)
print(f"mezclar {izq} y {der} → {a[desde:hasta]}")
def mezclar(a, desde, medio, hasta):
"""Mezcla a[desde:medio] y a[medio:hasta], ya ordenados, en a[desde:hasta]."""
tmp = []
i, j = desde, medio
while i < medio and j < hasta:
if a[i] <= a[j]: # <= : estable
tmp.append(a[i])
i += 1
else:
tmp.append(a[j])
j += 1
tmp += a[i:medio] + a[j:hasta] # lo que quede de una de las dos
a[desde:hasta] = tmp
a = [38, 27, 43, 3, 9, 82, 10]
mergesort(a, 0, len(a))
print("Resultado:", a)const texto = (a) => "[" + a.join(", ") + "]";
/** Ordena a[desde..hasta) y escribe cada mezcla que hace. */
function mergesort(a, desde, hasta) {
if (hasta - desde <= 1) return; // 0 o 1 elementos: ya está ordenado
const medio = Math.floor((desde + hasta) / 2);
mergesort(a, desde, medio); // ordena la mitad izquierda
mergesort(a, medio, hasta); // y la derecha
const izq = texto(a.slice(desde, medio));
const der = texto(a.slice(medio, hasta));
mezclar(a, desde, medio, hasta);
console.log(`mezclar ${izq} y ${der} → ${texto(a.slice(desde, hasta))}`);
}
/** Mezcla a[desde..medio) y a[medio..hasta), ya ordenados, en a[desde..hasta). */
function mezclar(a, desde, medio, hasta) {
const tmp = [];
let i = desde, j = medio;
while (i < medio && j < hasta) tmp.push(a[i] <= a[j] ? a[i++] : a[j++]); // <= : estable
while (i < medio) tmp.push(a[i++]); // lo que quede de la izquierda
while (j < hasta) tmp.push(a[j++]); // o de la derecha
a.splice(desde, tmp.length, ...tmp);
}
const a = [38, 27, 43, 3, 9, 82, 10];
mergesort(a, 0, a.length);
console.log("Resultado: " + texto(a));using System;
class Program {
static string Texto(int[] a, int desde, int hasta) => "[" + string.Join(", ", a[desde..hasta]) + "]";
// Ordena a[desde..hasta) y escribe cada mezcla que hace.
static void Mergesort(int[] a, int desde, int hasta) {
if (hasta - desde <= 1) return; // 0 o 1 elementos: ya está ordenado
int medio = (desde + hasta) / 2;
Mergesort(a, desde, medio); // ordena la mitad izquierda
Mergesort(a, medio, hasta); // y la derecha
string izq = Texto(a, desde, medio), der = Texto(a, medio, hasta);
Mezclar(a, desde, medio, hasta);
Console.WriteLine(quot;mezclar {izq} y {der} → {Texto(a, desde, hasta)}");
}
// Mezcla a[desde..medio) y a[medio..hasta), ya ordenados, en a[desde..hasta).
static void Mezclar(int[] a, int desde, int medio, int hasta) {
int[] tmp = new int[hasta - desde];
int i = desde, j = medio, k = 0;
while (i < medio && j < hasta) tmp[k++] = a[i] <= a[j] ? a[i++] : a[j++]; // <= : estable
while (i < medio) tmp[k++] = a[i++]; // lo que quede de la izquierda
while (j < hasta) tmp[k++] = a[j++]; // o de la derecha
Array.Copy(tmp, 0, a, desde, tmp.Length);
}
static void Main() {
int[] a = { 38, 27, 43, 3, 9, 82, 10 };
Mergesort(a, 0, a.Length);
Console.WriteLine("Resultado: " + Texto(a, 0, a.Length));
}
}<?php
function texto(array $a): string { return "[" . implode(", ", $a) . "]"; }
/** Ordena $a[$desde..$hasta) y escribe cada mezcla que hace. */
function mergesort(array &$a, int $desde, int $hasta): void {
if ($hasta - $desde <= 1) return; // 0 o 1 elementos: ya está ordenado
$medio = intdiv($desde + $hasta, 2);
mergesort($a, $desde, $medio); // ordena la mitad izquierda
mergesort($a, $medio, $hasta); // y la derecha
$izq = texto(array_slice($a, $desde, $medio - $desde));
$der = texto(array_slice($a, $medio, $hasta - $medio));
mezclar($a, $desde, $medio, $hasta);
echo "mezclar $izq y $der → " . texto(array_slice($a, $desde, $hasta - $desde)) . "\n";
}
/** Mezcla $a[$desde..$medio) y $a[$medio..$hasta), ya ordenados, en $a[$desde..$hasta). */
function mezclar(array &$a, int $desde, int $medio, int $hasta): void {
$tmp = [];
$i = $desde;
$j = $medio;
while ($i < $medio && $j < $hasta) $tmp[] = $a[$i] <= $a[$j] ? $a[$i++] : $a[$j++]; // <= : estable
while ($i < $medio) $tmp[] = $a[$i++]; // lo que quede de la izquierda
while ($j < $hasta) $tmp[] = $a[$j++]; // o de la derecha
array_splice($a, $desde, count($tmp), $tmp);
}
$a = [38, 27, 43, 3, 9, 82, 10];
mergesort($a, 0, count($a));
echo "Resultado: " . texto($a) . "\n";Salida al ejecutarlo (la misma en los 5 lenguajes)
mezclar [27] y [43] → [27, 43] mezclar [38] y [27, 43] → [27, 38, 43] mezclar [3] y [9] → [3, 9] mezclar [82] y [10] → [10, 82] mezclar [3, 9] y [10, 82] → [3, 9, 10, 82] mezclar [27, 38, 43] y [3, 9, 10, 82] → [3, 9, 10, 27, 38, 43, 82] Resultado: [3, 9, 10, 27, 38, 43, 82]
La ordenación estable de la biblioteca
Como la ordenación de objetos de la biblioteca es estable, ordenar primero por el criterio secundario y luego por el principal da el orden por los dos.
1// Para objetos, Java ordena con TimSort, un mergesort mejorado: es estable.
2List<Alumno> alumnos = cargarAlumnos();
3alumnos.sort(Comparator.comparing(Alumno::nombre)); // primero por nombre…
4alumnos.sort(Comparator.comparing(Alumno::curso)); // …y luego por curso: dentro de cada
5 // curso siguen ordenados por nombre# sorted() y list.sort() usan TimSort, un mergesort mejorado: son estables.
alumnos = cargar_alumnos()
alumnos.sort(key=lambda a: a.nombre) # primero por nombre…
alumnos.sort(key=lambda a: a.curso) # …y luego por curso: dentro de cada curso
# siguen ordenados por nombre// Array.prototype.sort es estable desde ES2019 (V8 usa TimSort).
const alumnos = cargarAlumnos();
alumnos.sort((a, b) => a.nombre.localeCompare(b.nombre)); // primero por nombre…
alumnos.sort((a, b) => a.curso - b.curso); // …y luego por curso: dentro de cada
// curso siguen ordenados por nombre// Ojo: List<T>.Sort NO es estable (usa introsort). OrderBy de LINQ sí lo es.
List<Alumno> alumnos = CargarAlumnos();
var ordenados = alumnos.OrderBy(a => a.Nombre) // primero por nombre…
.OrderBy(a => a.Curso) // …y luego por curso (estable)
.ToList();
// Lo habitual: alumnos.OrderBy(a => a.Curso).ThenBy(a => a.Nombre)// Desde PHP 8.0, sort, usort y compañía son estables.
$alumnos = cargarAlumnos();
usort($alumnos, fn($a, $b) => strcmp($a->nombre, $b->nombre)); // primero por nombre…
usort($alumnos, fn($a, $b) => $a->curso <=> $b->curso); // …y luego por curso: dentro de cada
// curso siguen ordenados por nombreTraza: mergesort de {38, 27, 43, 3, 9, 82, 10}
| Mezcla | Mitad izquierda | Mitad derecha | Resultado |
|---|---|---|---|
| 1 | [27] | [43] | [27, 43] |
| 2 | [38] | [27, 43] | [27, 38, 43] |
| 3 | [3] | [9] | [3, 9] |
| 4 | [82] | [10] | [10, 82] |
| 5 | [3, 9] | [10, 82] | [3, 9, 10, 82] |
| 6 | [27, 38, 43] | [3, 9, 10, 82] | [3, 9, 10, 27, 38, 43, 82] |
Seis mezclas para siete elementos (siempre n − 1). La última junta las dos mitades ordenadas del array entero.
Complejidad
| Datos (n) | Mergesort (≈ n log₂ n comparaciones) | Burbuja o inserción (≈ n²/2) |
|---|---|---|
| 1.000 | ≈ 10.000 | ≈ 500.000 |
| 100.000 | ≈ 1,7 millones | ≈ 5.000 millones |
| 10.000.000 | ≈ 230 millones | ≈ 50 billones |
O(n log n) en el mejor, el medio y el peor caso. Memoria extra: O(n) para el array auxiliar (más O(log n) de la pila de llamadas). Estable.
- Mejor caso: O(n log n)
- Caso medio: O(n log n)
- Peor caso: O(n log n)
Siempre n log n, a cambio de un array auxiliar de n elementos. Las curvas grises son las demás clases, para comparar.
En la práctica
- TimSort, el algoritmo de
Arrays.sortyCollections.sortpara objetos en Java, desorted()en Python y deArray.prototype.sorten V8, es un mergesort que aprovecha los tramos que ya vienen ordenados. - Las bases de datos ordenan tablas que no caben en memoria con un mergesort externo: ordenan bloques, los guardan en disco y los mezclan.
- La mezcla de dos listas ordenadas sirve por sí sola: unir dos ficheros ordenados, calcular la intersección de dos listas o hacer el JOIN por mezcla de un SGBD.
- Se paraleliza bien: cada mitad se puede ordenar en un hilo distinto (
Arrays.parallelSort).
Errores típicos
- Mezclar con
<en vez de<=: sigue ordenando, pero deja de ser estable (con empate coge primero el de la derecha). - Olvidar copiar lo que sobra de una de las mitades al acabar el bucle principal: se pierden elementos.
- Confundir los límites: con
hastaexclusivo, las mitades son[desde, medio)y[medio, hasta); si se mezcla con límites inclusivos y exclusivos a la vez, se repite o se pierde el elemento del medio. - Crear dos arrays nuevos en cada llamada (las mitades) en vez de trabajar sobre índices: funciona, pero gasta mucha más memoria y tiempo.
- Caso base mal puesto (
hasta - desde == 0): con un elemento se parte en un tramo vacío y otro de uno, y la recursividad no termina.
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. Mezcla y elementos comunes
Las dos primeras líneas son dos listas de enteros ordenadas de menor a mayor (pueden tener repetidos o estar vacías). Escribe su mezcla (todos los elementos, ordenados) y los valores que están en las dos (sin repetir). Las dos cosas se hacen en una sola pasada con dos índices, como la mezcla de mergesort: nada de ordenar después ni de bucles anidados. Completa mezclar y comunes.
- Entrada: línea 1 con la lista 1 y línea 2 con la lista 2, por ejemplo
1 3 5 5 8y2 3 5 9. - Salida:
Mezcla: [1, 2, 3, 3, 5, 5, 5, 8, 9]yComunes: [3, 5]; si no hay ninguno,(ninguno). - Errores:
Número no válido en la lista N: «x»yLa lista N no está ordenada.
Ejemplo
1 3 5 5 8 2 3 5 9
Mezcla: [1, 2, 3, 3, 5, 5, 5, 8, 9] Comunes: [3, 5]
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 /** Todos los elementos de a y b (ordenadas) en una lista ordenada, en una sola pasada. */
5 static List<Integer> mezclar(List<Integer> a, List<Integer> b) {
6 List<Integer> r = new ArrayList<>();
7 int i = 0, j = 0;
8 while (i < a.size() && j < b.size()) r.add(a.get(i) <= b.get(j) ? a.get(i++) : b.get(j++));
9 while (i < a.size()) r.add(a.get(i++));
10 while (j < b.size()) r.add(b.get(j++));
11 return r;
12 }
13
14 /** Los valores que están en las dos listas (sin repetir), también en una sola pasada. */
15 static List<Integer> comunes(List<Integer> a, List<Integer> b) {
16 List<Integer> r = new ArrayList<>();
17 int i = 0, j = 0;
18 while (i < a.size() && j < b.size()) {
19 int x = a.get(i), y = b.get(j);
20 if (x < y) i++;
21 else if (x > y) j++;
22 else {
23 if (r.isEmpty() || r.get(r.size() - 1) != x) r.add(x);
24 i++;
25 j++;
26 }
27 }
28 return r;
29 }
30
31 /** Lee una lista ordenada; null si hay un error (ya escrito). */
32 static List<Integer> leer(Scanner sc, int n) {
33 String linea = sc.hasNextLine() ? sc.nextLine().trim() : "";
34 List<Integer> l = new ArrayList<>();
35 if (linea.isEmpty()) return l;
36 for (String p : linea.split("\\s+")) {
37 try {
38 l.add(Integer.parseInt(p));
39 } catch (NumberFormatException e) {
40 System.out.println("Número no válido en la lista " + n + ": «" + p + "»");
41 return null;
42 }
43 }
44 for (int i = 1; i < l.size(); i++) {
45 if (l.get(i) < l.get(i - 1)) {
46 System.out.println("La lista " + n + " no está ordenada");
47 return null;
48 }
49 }
50 return l;
51 }
52
53 static String texto(List<Integer> l) {
54 return l.isEmpty() ? "(ninguno)" : l.toString();
55 }
56
57 public static void main(String[] args) {
58 Scanner sc = new Scanner(System.in);
59 List<Integer> a = leer(sc, 1);
60 if (a == null) return;
61 List<Integer> b = leer(sc, 2);
62 if (b == null) return;
63 System.out.println("Mezcla: " + texto(mezclar(a, b)));
64 System.out.println("Comunes: " + texto(comunes(a, b)));
65 }
66}Las dos funciones aprovechan que las listas están ordenadas: en cada paso se sabe con seguridad que el menor de los dos señalados ya no se va a necesitar más (o va a la mezcla, o no tiene pareja).
Es la misma técnica que el JOIN por mezcla de las bases de datos y que la intersección de listas de un buscador: lineal, en vez del n·m de comparar todos con todos.
2. Mergesort de abajo arriba
El mergesort también se puede hacer sin recursividad: primero se mezclan los elementos de dos en dos (bloques de 1), luego los bloques de 2 de dos en dos, luego los de 4… hasta que un bloque ocupa todo el array. Lee una línea de enteros y ordénala así, mostrando el array tras cada nivel. La mezcla ya está hecha: completa nivel, que mezcla cada pareja de bloques consecutivos de un tamaño dado.
- Entrada: una línea de enteros separados por espacios.
- Por cada nivel:
Bloques de 1 mezclados de dos en dos: 27 38 3 43 …; al final,Ordenado en N niveles: …. - Errores:
No hay númerosyNúmero no válido: «x».
Ejemplo
38 27 43 3 9 82 10
Bloques de 1 mezclados de dos en dos: 27 38 3 43 9 82 10 Bloques de 2 mezclados de dos en dos: 3 27 38 43 9 10 82 Bloques de 4 mezclados de dos en dos: 3 9 10 27 38 43 82 Ordenado en 3 niveles: 3 9 10 27 38 43 82
Ver la solución explicada
1import java.util.*;
2
3public class Main {
4 /** Mezcla a[desde..medio) y a[medio..hasta), ya ordenados. */
5 static void mezclar(int[] a, int desde, int medio, int hasta) {
6 int[] tmp = new int[hasta - desde];
7 int i = desde, j = medio, k = 0;
8 while (i < medio && j < hasta) tmp[k++] = a[i] <= a[j] ? a[i++] : a[j++];
9 while (i < medio) tmp[k++] = a[i++];
10 while (j < hasta) tmp[k++] = a[j++];
11 System.arraycopy(tmp, 0, a, desde, tmp.length);
12 }
13
14 /** Un nivel del mergesort de abajo arriba: mezcla cada pareja de bloques consecutivos de tamaño
15 ancho (el último bloque puede ser más corto o no tener pareja). */
16 static void nivel(int[] a, int ancho) {
17 for (int desde = 0; desde < a.length; desde += 2 * ancho) {
18 int medio = Math.min(desde + ancho, a.length);
19 int hasta = Math.min(desde + 2 * ancho, a.length);
20 if (medio < hasta) mezclar(a, desde, medio, hasta);
21 }
22 }
23
24 /** Lee una línea de enteros; si falla, escribe el error y devuelve null. */
25 static int[] leer(Scanner sc) {
26 String linea = sc.hasNextLine() ? sc.nextLine().trim() : "";
27 if (linea.isEmpty()) {
28 System.out.println("No hay números");
29 return null;
30 }
31 String[] partes = linea.split("\\s+");
32 int[] a = new int[partes.length];
33 for (int i = 0; i < partes.length; i++) {
34 try {
35 a[i] = Integer.parseInt(partes[i]);
36 } catch (NumberFormatException e) {
37 System.out.println("Número no válido: «" + partes[i] + "»");
38 return null;
39 }
40 }
41 return a;
42 }
43
44 static String texto(int[] a) {
45 StringJoiner sj = new StringJoiner(" ");
46 for (int x : a) sj.add(String.valueOf(x));
47 return sj.toString();
48 }
49
50 public static void main(String[] args) {
51 Scanner sc = new Scanner(System.in);
52 int[] a = leer(sc);
53 if (a == null) return;
54 int niveles = 0;
55 for (int ancho = 1; ancho < a.length; ancho *= 2) {
56 nivel(a, ancho);
57 niveles++;
58 System.out.println("Bloques de " + ancho + " mezclados de dos en dos: " + texto(a));
59 }
60 System.out.println("Ordenado en " + niveles + (niveles == 1 ? " nivel: " : " niveles: ") + texto(a));
61 }
62}Es el mismo trabajo que el mergesort recursivo, en otro orden: en vez de bajar hasta los trozos de 1 y volver mezclando, se empieza por ellos. Hay ⌈log₂ n⌉ niveles y cada uno recorre el array una vez.
Sin recursividad no hay riesgo de desbordar la pila, y es la base de la ordenación externa: cada nivel es una pasada sobre los ficheros.
Test
Test: Mergesort (ordenación por mezcla)
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ál es el coste de mergesort en el peor caso?
2.¿Qué le hace falta a mergesort que no necesitan la burbuja ni el quicksort?
3.Para que mergesort sea estable, ante un empate la mezcla debe coger…
4.¿Cuántas mezclas hace mergesort sobre 8 elementos?
5.¿Por qué Java usa un algoritmo basado en mergesort para ordenar objetos?