Implementazione Javascript Array.sort?


236

Quale algoritmo utilizza la Array#sort()funzione JavaScript ? Capisco che possono essere necessari tutti i tipi di argomenti e funzioni per eseguire diversi tipi, sono semplicemente interessato a quale algoritmo viene utilizzato dal tipo vanilla.


Dovresti considerare una soluzione alternativa rispetto a quelle fornite.
Anthony,

Risposte:


77

Se guardi questo bug 224128 , sembra che MergeSort sia utilizzato da Mozilla.


3
Beh, è ​​anche sbagliato in quanto indica solo un algoritmo per un'implementazione specifica. La specifica non fa affermazioni del genere e altre implementazioni utilizzano altri algoritmi, quindi è abbastanza fuorviante.
Patrick Roberts,

292

Ho appena dato un'occhiata alla fonte WebKit (Chrome, Safari ...) . A seconda del tipo di array, vengono utilizzati diversi metodi di ordinamento:

Le matrici numeriche (o matrici di tipo primitivo) sono ordinate usando la funzione di libreria standard C ++ std::qsortche implementa alcune variazioni di quicksort (generalmente introsort ).

Le matrici contigue di tipo non numerico vengono stringite e ordinate utilizzando mergesort, se disponibile (per ottenere un ordinamento stabile) o qsortse non è disponibile alcun ordinamento di unione.

Per altri tipi (array non contigui e presumibilmente per array associativi) WebKit utilizza un ordinamento di selezione (che chiamano ordinamento "min" ) o, in alcuni casi, ordina tramite un albero AVL. Sfortunatamente, la documentazione qui è piuttosto vaga, quindi dovresti tracciare i percorsi del codice per vedere effettivamente per quali tipi viene utilizzato il metodo di ordinamento.

E poi ci sono gemme come questo commento :

// 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).

- Speriamo solo che chiunque "risolva" effettivamente abbia una migliore comprensione del runtime asintotico rispetto allo scrittore di questo commento e realizzi che l' ordinamento radix ha una descrizione del runtime leggermente più complessa rispetto alla semplice O (N).

(Grazie a phsource per aver segnalato l'errore nella risposta originale.)


46

Non è richiesto alcun progetto di bozza per JS di utilizzare un algoritmo di ordinamento specifico. Come molti hanno già menzionato qui, Mozilla utilizza il merge sort.Tuttavia, nel codice sorgente v8 di Chrome, ad oggi, utilizza QuickSort e InsertionSort, per array più piccoli.

V8 Engine Source

Dalle linee 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;
      }
    }
  };

Aggiornamento A partire dal 2018 V8 utilizza TimSort, grazie a @celwell. fonte



24

Lo standard ECMAscript non specifica quale algoritmo di ordinamento deve essere utilizzato. In effetti, diversi browser presentano algoritmi di ordinamento diversi. Ad esempio, l'ordinamento () di Mozilla / Firefox non è stabile (nel senso dell'ordinamento della parola) durante l'ordinamento di una mappa. Sort () di IE è stabile.


15
Aggiornamento: Firefox recenti hanno una stabile Array.sort; vedi questa domanda .
skagedal,

Il punto è che l'algoritmo di ordinamento dipende dall'implementazione.
sean,

Per i curiosi, lo standard ECMAscript si trova qui: tc39.github.io/ecma262/#sec-array.prototype.sort
Benjamin

10

Dopo alcune ulteriori ricerche, sembra, per Mozilla / Firefox, che Array.sort () utilizzi mergesort. Vedi il codice qui .


8

Penso che dipenderebbe dall'implementazione del browser a cui ti riferisci.

Ogni tipo di browser ha la propria implementazione del motore javascript, quindi dipende. È possibile controllare i repository del codice sorgente per Mozilla e Webkit / Khtml per diverse implementazioni.

IE è comunque chiuso, quindi potrebbe essere necessario chiedere a qualcuno di Microsoft.


Interpreti diversi possono fare le cose in modo diverso nel senso che sono o buggy (cioè non è intenzionale) o aggiungono o tolgono funzionalità. Il metodo sort () è una parte standard di Core JavaScript e sarebbe definito dallo standard, che i browser vorrebbero seguire.
Jason Bunting,

2
@JasonBunting se la funzione è implementata e fa quello che dovrebbe fare come definito nelle specifiche, gli sviluppatori di browser sono liberi di implementare la funzione come vogliono: che si tratti di bolle o ordinamento rapido. Le specifiche ECMA non definiscono l'algoritmo di ordinamento da utilizzare.
Damir Zekić,


0

La funzione Array.sort () di JavaScript ha meccanismi interni per selezionare il miglior algoritmo di ordinamento (QuickSort, MergeSort, ecc.) Sulla base del tipo di dati degli elementi dell'array.


0

prova questo con ordinamento rapido:

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.