Implementación Javascript Array.sort?

236

¿Qué algoritmo usa la Array#sort()función JavaScript ? Entiendo que puede tomar todo tipo de argumentos y funciones para realizar diferentes tipos de tipos, simplemente estoy interesado en qué algoritmo utiliza el tipo de vainilla.

latortuga
fuente
Debe considerar una solución alternativa de las que se dan.
Anthony

Respuestas:

77

Si observa este error 224128 , parece que Mogeilla está utilizando MergeSort.

Britton
fuente
3
Bueno, también está mal porque solo establece un algoritmo para una implementación específica. La especificación no hace tales afirmaciones, y otras implementaciones usan otros algoritmos, por lo que esto es bastante engañoso.
Patrick Roberts el
292

Acabo de echar un vistazo a la fuente WebKit (Chrome, Safari ...) . Dependiendo del tipo de matriz, se utilizan diferentes métodos de clasificación:

Las matrices numéricas (o matrices de tipo primitivo) se ordenan utilizando la función de biblioteca estándar C ++ std::qsortque implementa alguna variación de clasificación rápida (generalmente introsort ).

Las matrices contiguas de tipo no numérico se clasifican en cadena y se clasifican utilizando mergesort, si está disponible (para obtener una clasificación estable) oqsort si no hay una clasificación de fusión disponible.

Para otros tipos (matrices no contiguas y presumiblemente para matrices asociativas) WebKit utiliza cualquier tipo de selección (que denominan clasificación "min" ) o, en algunos casos, se ordena a través de un árbol AVL. Desafortunadamente, la documentación aquí es bastante vaga, por lo que tendrías que rastrear las rutas de código para ver qué tipos de método de clasificación se utilizan realmente.

Y luego hay gemas como este comentario :

// FIXME: Since we sort by string value, a fast algorithm might be to use a
// radix sort. That would be O(N) rather than O(N log N).

- Esperemos que quien realmente "arregle" esto comprenda mejor el tiempo de ejecución asintótico que el autor de este comentario, y se dé cuenta de que la clasificación de radix tiene una descripción de tiempo de ejecución un poco más compleja que simplemente O (N).

(Gracias a phsource por señalar el error en la respuesta original).

Konrad Rudolph
fuente
46

No hay un requisito de borrador para que JS use un algoritmo de clasificación específico. Como muchos han mencionado aquí, Mozilla usa el tipo de fusión. Sin embargo, en el código fuente v8 de Chrome, a partir de hoy, usa QuickSort e InsertionSort, para arreglos más pequeños.

Fuente del motor V8

Desde las líneas 807 - 891

  var QuickSort = function QuickSort(a, from, to) {
    var third_index = 0;
    while (true) {
      // Insertion sort is faster for short arrays.
      if (to - from <= 10) {
        InsertionSort(a, from, to);
        return;
      }
      if (to - from > 1000) {
        third_index = GetThirdIndex(a, from, to);
      } else {
        third_index = from + ((to - from) >> 1);
      }
      // Find a pivot as the median of first, last and middle element.
      var v0 = a[from];
      var v1 = a[to - 1];
      var v2 = a[third_index];
      var c01 = comparefn(v0, v1);
      if (c01 > 0) {
        // v1 < v0, so swap them.
        var tmp = v0;
        v0 = v1;
        v1 = tmp;
      } // v0 <= v1.
      var c02 = comparefn(v0, v2);
      if (c02 >= 0) {
        // v2 <= v0 <= v1.
        var tmp = v0;
        v0 = v2;
        v2 = v1;
        v1 = tmp;
      } else {
        // v0 <= v1 && v0 < v2
        var c12 = comparefn(v1, v2);
        if (c12 > 0) {
          // v0 <= v2 < v1
          var tmp = v1;
          v1 = v2;
          v2 = tmp;
        }
      }
      // v0 <= v1 <= v2
      a[from] = v0;
      a[to - 1] = v2;
      var pivot = v1;
      var low_end = from + 1;   // Upper bound of elements lower than pivot.
      var high_start = to - 1;  // Lower bound of elements greater than pivot.
      a[third_index] = a[low_end];
      a[low_end] = pivot;

      // From low_end to i are elements equal to pivot.
      // From i to high_start are elements that haven't been compared yet.
      partition: for (var i = low_end + 1; i < high_start; i++) {
        var element = a[i];
        var order = comparefn(element, pivot);
        if (order < 0) {
          a[i] = a[low_end];
          a[low_end] = element;
          low_end++;
        } else if (order > 0) {
          do {
            high_start--;
            if (high_start == i) break partition;
            var top_elem = a[high_start];
            order = comparefn(top_elem, pivot);
          } while (order > 0);
          a[i] = a[high_start];
          a[high_start] = element;
          if (order < 0) {
            element = a[i];
            a[i] = a[low_end];
            a[low_end] = element;
            low_end++;
          }
        }
      }
      if (to - high_start < low_end - from) {
        QuickSort(a, high_start, to);
        to = low_end;
      } else {
        QuickSort(a, from, low_end);
        from = high_start;
      }
    }
  };

Actualización A partir de 2018 V8 usa TimSort, gracias @celwell. Fuente

Joe Thomas
fuente
99
Creo que V8 ahora está usando TimSort: github.com/v8/v8/blob/78f2610345fdd14ca401d920c140f8f461b631d1/…
celwell el
24

El estándar ECMAscript no especifica qué algoritmo de clasificación se utilizará. De hecho, diferentes navegadores presentan diferentes algoritmos de clasificación. Por ejemplo, sort () de Mozilla / Firefox no es estable (en el sentido de clasificación de la palabra) al ordenar un mapa. El tipo de IE () es estable.


fuente
15
Actualización: los Firefox recientes tienen una estabilidad Array.sort; ver a esta pregunta .
skagedal
El punto es que el algoritmo de clasificación depende de la implementación.
sean
Para los curiosos, el estándar ECMAscript se encuentra aquí: tc39.github.io/ecma262/#sec-array.prototype.sort
Benjamin
10

Después de un poco más de investigación, parece, para Mozilla / Firefox, que Array.sort () usa mergesort. Mira el código aquí .

latortuga
fuente
8

Creo que eso dependerá de a qué implementación de navegador se refiera.

Cada tipo de navegador tiene su propia implementación de motor javascript, por lo que depende. Puede verificar los repositorios de código fuente para Mozilla y Webkit / Khtml para diferentes implementaciones.

Sin embargo, IE es de código cerrado, por lo que es posible que deba consultar a alguien en Microsoft.

Huibert Gill
fuente
Los diferentes intérpretes pueden hacer las cosas de manera diferente en el sentido de que tienen errores (es decir, no son a propósito) o agregan o quitan funciones. El método sort () es una parte estándar de Core JavaScript y estaría definido por el estándar, que los navegadores querrían seguir.
Jason Bunting
2
@JasonBunting si la función se implementa y hace lo que debería hacer según lo definido en la especificación, los desarrolladores de navegadores son libres de implementar la función como lo deseen: ya sea burbuja o clasificación rápida. Las especificaciones de ECMA no definen el algoritmo de clasificación que se utilizará.
Damir Zekić
0

La función Array.sort () de JavaScript tiene mecanismos internos para seleccionar el mejor algoritmo de ordenación (QuickSort, MergeSort, etc.) en función del tipo de datos de los elementos de la matriz.

Abhijit Srivastava
fuente
0

intente esto con una ordenación rápida:

function sort(arr, compareFn = (a, b) => a <= b) {

    if (!arr instanceof Array || arr.length === 0) {
        return arr;
    }

    if (typeof compareFn !== 'function') {
        throw new Error('compareFn is not a function!');
    }

    const partition = (arr, low, high) => {
        const pivot = arr[low];
        while (low < high) {
            while (low < high && compareFn(pivot, arr[high])) {
                --high;
            }
            arr[low] = arr[high];
            while (low < high && compareFn(arr[low], pivot)) {
                ++low;
            }
            arr[high] = arr[low];
        }
        arr[low] = pivot;
        return low;
    };

    const quickSort = (arr, low, high) => {
        if (low < high) {
            let pivot = partition(arr, low, high);
            quickSort(arr, low, pivot - 1);
            quickSort(arr, pivot + 1, high);
        }
        return arr;
    };

    return quickSort(arr, 0, arr.length - 1);
}
un punto
fuente