Implementazione veloce dell'algoritmo di ordinamento stabile in javascript


104

Sto cercando di ordinare un array di circa 200-300 oggetti, ordinando su una chiave specifica e un determinato ordine (asc / desc). L'ordine dei risultati deve essere coerente e stabile.

Quale sarebbe il miglior algoritmo da utilizzare e potresti fornire un esempio della sua implementazione in javascript?

Grazie!


6
Poiché almeno l'ordinamento dell'array di Chrome non sembra essere stabile, fare affidamento sull'ordinamento dell'array integrato non è un'opzione per te.
Nosredna

Per riassumere: sono andato con un ordinamento di fusione a mano a causa di incongruenze di stabilità di Array.sort tra i browser moderni (principalmente Chrome non implementa un ordinamento stabile al momento di questo commento). Grazie a tutti per il vostro aiuto!
William Casarin

Cosa si intende per ordinamento "stabile"?
mowwwalker

2
@mowwwalker L'ordinamento stabile è un ordinamento in cui tutti gli elementi con lo stesso valore di ordinamento vengono lasciati nello stesso ordine della raccolta originale. en.wikipedia.org/wiki/Sorting_algorithm#Stability
Kornelije Petak

Per rispondere "qual è il miglior algoritmo da utilizzare" dobbiamo sapere se esiste una struttura sottostante ai tuoi dati. Molte delle risposte seguenti parlano solo dell'utilizzo dell'ordinamento di tipo merge o dell'ordinamento rapido, in realtà dipende dai dati. Non è un problema semplice rispondere solo non direi. Google alcuni algoritmi di ordinamento e leggere su di loro per vedere cosa intendo. TimSort e Radix Sort sono due buoni esempi di cui consiglierei di leggere.
sarà il

Risposte:


114

È possibile ottenere un ordinamento stabile da una funzione di ordinamento non stabile.

Prima di ordinare si ottiene la posizione di tutti gli elementi. Nella condizione di ordinamento, se entrambi gli elementi sono uguali, si ordina in base alla posizione.

Tada! Hai un tipo stabile.

Ho scritto un articolo a riguardo sul mio blog se vuoi saperne di più su questa tecnica e su come implementarla: http://blog.vjeux.com/2010/javascript/javascript-sorting-table.html


32
Potresti fornire la risposta qui invece di postare e collegare il tuo blog! Grazie
Mohammad Kermani

4
Un collegamento a una soluzione è il benvenuto, ma assicurati che la tua risposta sia utile senza di essa: aggiungi un contesto attorno al collegamento in modo che i tuoi colleghi utenti abbiano un'idea di cosa sia e perché sia ​​presente, quindi cita la parte più pertinente della pagina che tu " re collegamento a nel caso in cui la pagina di destinazione non è disponibile. Le risposte che sono poco più di un collegamento possono essere eliminate.
Samuel Liew

@ Vjeux poiché sta diventando popolare, ti dispiace incollare il codice pertinente in questa risposta? Aiuterebbe molto! Grazie!
William Casarin

34

Dato che stai cercando qualcosa di stabile, il merge sort dovrebbe andare bene.

http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/

Il codice può essere trovato sul sito web sopra:

function mergeSort(arr)
{
    if (arr.length < 2)
        return arr;

    var middle = parseInt(arr.length / 2);
    var left   = arr.slice(0, middle);
    var right  = arr.slice(middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
    var result = [];

    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

MODIFICARE:

Secondo questo post , sembra che Array.Sort in alcune implementazioni utilizzi un merge sort.


++ Merge sort è il mio preferito. È semplice e stabile senza casi peggiori.
Mike Dunlavey,

Il link al sito web non è
attivo

trovato un nuovo sito web per l'esempio.
kemiller2002

7
Nota: Array#shiftpuò funzionare in O (n) tempo e quindi mergein O (n * n).
4esn0k

22

Versione un po 'più breve della stessa cosa utilizzando le funzionalità di ES2017 come le funzioni freccia e la destrutturazione:

Funzione

var stableSort = (arr, compare) => arr
  .map((item, index) => ({item, index}))
  .sort((a, b) => compare(a.item, b.item) || a.index - b.index)
  .map(({item}) => item)

Accetta array di input e funzioni di confronto:

stableSort([5,6,3,2,1], (a, b) => a - b)

Restituisce anche un nuovo array invece di effettuare un ordinamento sul posto come la funzione Array.sort () incorporata.

Test

Se prendiamo il seguente inputarray, inizialmente ordinato per weight:

// sorted by weight
var input = [
  { height: 100, weight: 80 },
  { height: 90, weight: 90 },
  { height: 70, weight: 95 },
  { height: 100, weight: 100 },
  { height: 80, weight: 110 },
  { height: 110, weight: 115 },
  { height: 100, weight: 120 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 100, weight: 135 },
  { height: 75, weight: 140 },
  { height: 70, weight: 140 }
]

Quindi ordinalo heightusando stableSort:

stableSort(input, (a, b) => a.height - b.height)

Risultati in:

// Items with the same height are still sorted by weight 
// which means they preserved their relative order.
var stable = [
  { height: 70, weight: 95 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 70, weight: 140 },
  { height: 75, weight: 140 },
  { height: 80, weight: 110 },
  { height: 90, weight: 90 },
  { height: 100, weight: 80 },
  { height: 100, weight: 100 },
  { height: 100, weight: 120 },
  { height: 100, weight: 135 },
  { height: 110, weight: 115 }
]

Tuttavia, ordinamento dello stesso inputarray utilizzando il built-in Array.sort()(in Chrome / NodeJS):

input.sort((a, b) => a.height - b.height)

Ritorna:

var unstable = [
  { height: 70, weight: 140 },
  { height: 70, weight: 95 },
  { height: 70, weight: 125 },
  { height: 70, weight: 130 },
  { height: 75, weight: 140 },
  { height: 80, weight: 110 },
  { height: 90, weight: 90 },
  { height: 100, weight: 100 },
  { height: 100, weight: 80 },
  { height: 100, weight: 135 },
  { height: 100, weight: 120 },
  { height: 110, weight: 115 }
]

risorse

Aggiornare

Array.prototype.sort è ora stabile in V8 v7.0 / Chrome 70!

In precedenza, V8 utilizzava un QuickSort instabile per array con più di 10 elementi. Ora usiamo l'algoritmo TimSort stabile.

fonte


1
La stableSortfunzione è davvero un'ottima soluzione!
lhermann il

16

So che questa domanda è stata risolta per un po 'di tempo, ma mi capita di avere una buona implementazione di merge sort stabile per Array e jQuery nei miei appunti, quindi la condividerò nella speranza che alcuni futuri ricercatori potrebbero trovarla utile.

Ti consente di specificare la tua funzione di confronto proprio come la normale Array.sortimplementazione.

Implementazione

// Add stable merge sort to Array and jQuery prototypes
// Note: We wrap it in a closure so it doesn't pollute the global
//       namespace, but we don't put it in $(document).ready, since it's
//       not dependent on the DOM
(function() {

  // expose to Array and jQuery
  Array.prototype.mergeSort = jQuery.fn.mergeSort = mergeSort;

  function mergeSort(compare) {

    var length = this.length,
        middle = Math.floor(length / 2);

    if (!compare) {
      compare = function(left, right) {
        if (left < right)
          return -1;
        if (left == right)
          return 0;
        else
          return 1;
      };
    }

    if (length < 2)
      return this;

    return merge(
      this.slice(0, middle).mergeSort(compare),
      this.slice(middle, length).mergeSort(compare),
      compare
    );
  }

  function merge(left, right, compare) {

    var result = [];

    while (left.length > 0 || right.length > 0) {
      if (left.length > 0 && right.length > 0) {
        if (compare(left[0], right[0]) <= 0) {
          result.push(left[0]);
          left = left.slice(1);
        }
        else {
          result.push(right[0]);
          right = right.slice(1);
        }
      }
      else if (left.length > 0) {
        result.push(left[0]);
        left = left.slice(1);
      }
      else if (right.length > 0) {
        result.push(right[0]);
        right = right.slice(1);
      }
    }
    return result;
  }
})();

Utilizzo di esempio

var sorted = [
  'Finger',
  'Sandwich',
  'sandwich',
  '5 pork rinds',
  'a guy named Steve',
  'some noodles',
  'mops and brooms',
  'Potato Chip Brand® chips'
].mergeSort(function(left, right) {
  lval = left.toLowerCase();
  rval = right.toLowerCase();

  console.log(lval, rval);
  if (lval < rval)
    return -1;
  else if (lval == rval)
    return 0;
  else
    return 1;
});

sorted == ["5 pork rinds", "a guy named Steve", "Finger", "mops and brooms", "Potato Chip Brand® chips", "Sandwich", "sandwich", "some noodles"];

4
Nota che questo è in contrasto con l'ordinamento nativo, che funziona sul posto, il che significa che questo non può essere semplicemente inserito.
Eric

3
Forse stabile, ma questo metodo riguarda 20 volte più lento del nativo array.sort, vedi il test qui sia per le stringhe che per i numeri interi -> jsfiddle.net/QC64j
davidkonrad

2
Ovviamente è più lento dell'ordinamento nativo, non è nativo. È impossibile. È anche vero che non esegue un ordinamento sul posto. Anche questo è impossibile (nel migliore dei casi si crea una copia e si sovrascrive l'originale). Inoltre, la restituzione di una copia ordinata è più JavaScript-y nonostante il comportamento di ordinamento nativo di JavaScript. La funzione è anche chiamata mergeSorte non sort, quindi non è intesa come un calo di sostituzione. A volte hai solo bisogno di un merge sort stabile, ad esempio quando si ordinano le tabelle per colonna.
Justin Force

1
Sbagliato, l'ordinamento nativo di Node è scritto in javascript. È del tutto possibile per un algoritmo programmato in javascript per velocizzare l'ordinamento nativo. Ho costruito un algoritmo di ordinamento interamente in javascript (un tipo di ordinamento di unione adattivo) che Kremes / creams / Kreams Il quicksort nativo in node. Il punto di questo commento è mostrare che il nativo non ha importanza nel caso di javascript perché l'algoritmo di ordinamento è scritto in javascript e non in un linguaggio superiore come c ++. La prova è qui: github.com/nodejs/node/blob/master/deps/v8/src/js/array.js
ahitt6345

2
Per chiunque desideri una soluzione immediata e immediata che sia molto più veloce di questa implementazione, controlla la mia risposta .
Patrick Roberts

10

Puoi utilizzare il seguente polyfill per implementare un ordinamento stabile indipendentemente dall'implementazione nativa, in base all'asserzione fatta in questa risposta :

// ECMAScript 5 polyfill
Object.defineProperty(Array.prototype, 'stableSort', {
  configurable: true,
  writable: true,
  value: function stableSort (compareFunction) {
    'use strict'

    var length = this.length
    var entries = Array(length)
    var index

    // wrap values with initial indices
    for (index = 0; index < length; index++) {
      entries[index] = [index, this[index]]
    }

    // sort with fallback based on initial indices
    entries.sort(function (a, b) {
      var comparison = Number(this(a[1], b[1]))
      return comparison || a[0] - b[0]
    }.bind(compareFunction))

    // re-map original array to stable sorted values
    for (index = 0; index < length; index++) {
      this[index] = entries[index][1]
    }
    
    return this
  }
})

// usage
const array = Array(500000).fill().map(() => Number(Math.random().toFixed(4)))

const alwaysEqual = () => 0
const isUnmoved = (value, index) => value === array[index]

// not guaranteed to be stable
console.log('sort() stable?', array
  .slice()
  .sort(alwaysEqual)
  .every(isUnmoved)
)
// guaranteed to be stable
console.log('stableSort() stable?', array
  .slice()
  .stableSort(alwaysEqual)
  .every(isUnmoved)
)

// performance using realistic scenario with unsorted big data
function time(arrayCopy, algorithm, compare) {
  var start
  var stop
  
  start = performance.now()
  algorithm.call(arrayCopy, compare)
  stop = performance.now()
  
  return stop - start
}

const ascending = (a, b) => a - b

const msSort = time(array.slice(), Array.prototype.sort, ascending)
const msStableSort = time(array.slice(), Array.prototype.stableSort, ascending)

console.log('sort()', msSort.toFixed(3), 'ms')
console.log('stableSort()', msStableSort.toFixed(3), 'ms')
console.log('sort() / stableSort()', (100 * msSort / msStableSort).toFixed(3) + '%')

L'esecuzione dei test delle prestazioni implementati sopra, stableSort()sembra funzionare a circa il 57% della velocità della sort()versione 59-61 di Chrome.

L'utilizzo .bind(compareFunction)della funzione anonima racchiusa all'interno ha stableSort()aumentato le prestazioni relative da circa il 38% evitando un riferimento con ambito non necessario a compareFunctionogni chiamata assegnandola invece al contesto.

Aggiornare

Operatore ternario modificato in cortocircuito logico, che tende a funzionare meglio in media (sembra fare una differenza del 2-3% nell'efficienza).


5

Quanto segue ordina l'array fornito, applicando la funzione di confronto fornita, restituendo il confronto dell'indice originale quando la funzione di confronto restituisce 0:

function stableSort(arr, compare) {
    var original = arr.slice(0);

    arr.sort(function(a, b){
        var result = compare(a, b);
        return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
    });

    return arr;
}

L'esempio seguente ordina un array di nomi in base al cognome, mantenendo l'ordine dei cognomi uguali:

var names = [
	{ surname: "Williams", firstname: "Mary" },
	{ surname: "Doe", firstname: "Mary" }, 
	{ surname: "Johnson", firstname: "Alan" }, 
	{ surname: "Doe", firstname: "John" }, 
	{ surname: "White", firstname: "John" }, 
	{ surname: "Doe", firstname: "Sam" }
]

function stableSort(arr, compare) {
    var original = arr.slice(0);

    arr.sort(function(a, b){
        var result = compare(a, b);
        return result === 0 ? original.indexOf(a) - original.indexOf(b) : result;
    });
	
    return arr;
}

stableSort(names, function(a, b) { 
	return a.surname > b.surname ? 1 : a.surname < b.surname ? -1 : 0;
})

names.forEach(function(name) {
	console.log(name.surname + ', ' + name.firstname);
});


Non stabile per tipi primitivi o elementi duplicati in array. jQuery.expando.split( "" ).sort( ( a, b ) => 0 ).join( "" ) === jQuery.expando
marziano

3

Ecco un'implementazione stabile. Funziona utilizzando l'ordinamento nativo, ma nei casi in cui gli elementi vengono confrontati allo stesso modo, si interrompono i legami utilizzando la posizione dell'indice originale.

function stableSort(arr, cmpFunc) {
    //wrap the arr elements in wrapper objects, so we can associate them with their origional starting index position
    var arrOfWrapper = arr.map(function(elem, idx){
        return {elem: elem, idx: idx};
    });

    //sort the wrappers, breaking sorting ties by using their elements orig index position
    arrOfWrapper.sort(function(wrapperA, wrapperB){
        var cmpDiff = cmpFunc(wrapperA.elem, wrapperB.elem);
        return cmpDiff === 0 
             ? wrapperA.idx - wrapperB.idx
             : cmpDiff;
    });

    //unwrap and return the elements
    return arrOfWrapper.map(function(wrapper){
        return wrapper.elem;
    });
}

un test non approfondito

var res = stableSort([{a:1, b:4}, {a:1, b:5}], function(a, b){
    return a.a - b.a;
});
console.log(res);

un'altra risposta alludeva a questo, ma non postò il codice.

ma non è veloce secondo il mio benchmark . Ho modificato un impl di merge sort per accettare una funzione di comparazione personalizzata ed è stato molto più veloce.


Il tuo benchmark è corretto? Sembra che il tuo "stableSort" non modifichi l'array di input, altri tipi - fallo, e poiché non hai ricreato "arr" durante "setup", altri ordinamenti ordinano gli array già ordinati ....
4esn0k

@ 4esn0k hai letto male? Ho detto che la mia funzione stableSort NON era veloce.
capra

@gota, ah, perdonami
4esn0k

3

Puoi anche usare Timsort. Questo è un algoritmo davvero complicato (oltre 400 righe, quindi nessun codice sorgente qui), quindi vedi la descrizione di Wikipedia o usa una delle implementazioni JavaScript esistenti:

Implementazione della GPL 3 . Confezionato come Array.prototype.timsort. Sembra essere una riscrittura esatta del codice Java.

Implementazione di dominio pubblico Inteso come tutorial, il codice di esempio mostra solo il suo utilizzo con numeri interi.

Timsort è un ibrido altamente ottimizzato di mergesort e shuffle sort ed è l'algoritmo di ordinamento predefinito in Python e in Java (1.7+). È un algoritmo complicato, poiché utilizza algoritmi diversi per molti casi speciali. Ma di conseguenza è estremamente veloce in un'ampia varietà di circostanze.


1

Un semplice mergeSort da http://www.stoimen.com/blog/2010/07/02/friday-algorithms-javascript-merge-sort/

var a = [34, 203, 3, 746, 200, 984, 198, 764, 9];

function mergeSort(arr)
{
    if (arr.length < 2)
         return arr;

    var middle = parseInt(arr.length / 2);
    var left   = arr.slice(0, middle);
    var right  = arr.slice(middle, arr.length);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right)
{
     var result = [];

    while (left.length && right.length) {
         if (left[0] <= right[0]) {
             result.push(left.shift());
         } else {
            result.push(right.shift());
         }
    }

    while (left.length)
        result.push(left.shift());

    while (right.length)
        result.push(right.shift());

    return result;
}

console.log(mergeSort(a));

0

Devo ordinare gli array multidimensionali da una colonna arbitraria e poi da un'altra. Uso questa funzione per ordinare:

function sortMDArrayByColumn(ary, sortColumn){

    //Adds a sequential number to each row of the array
    //This is the part that adds stability to the sort
    for(var x=0; x<ary.length; x++){ary[x].index = x;}

    ary.sort(function(a,b){
        if(a[sortColumn]>b[sortColumn]){return 1;}
        if(a[sortColumn]<b[sortColumn]){return -1;}
        if(a.index>b.index){
            return 1;
        }
        return -1;
    });
}

Si noti che ary.sort non restituisce mai zero, che è il punto in cui alcune implementazioni della funzione "sort" prendono decisioni che potrebbero non essere corrette.

Anche questo è dannatamente veloce.


0

Ecco come estendere l'oggetto Array predefinito di JS con un metodo prototipo utilizzando MERGE SORT . Questo metodo consente l'ordinamento su una chiave specifica (primo parametro) e un determinato ordine ('asc' / 'desc' come secondo parametro)

Array.prototype.mergeSort = function(sortKey, direction){
  var unsortedArray = this;
  if(unsortedArray.length < 2) return unsortedArray;

  var middle = Math.floor(unsortedArray.length/2);
  var leftSubArray = unsortedArray.slice(0,middle).mergeSort(sortKey, direction);
  var rightSubArray = unsortedArray.slice(middle).mergeSort(sortKey, direction);

  var sortedArray = merge(leftSubArray, rightSubArray);
  return sortedArray;

  function merge(left, right) {
    var combined = [];
    while(left.length>0 && right.length>0){
      var leftValue = (sortKey ? left[0][sortKey] : left[0]);
      var rightValue = (sortKey ? right[0][sortKey] : right[0]);
      combined.push((direction === 'desc' ? leftValue > rightValue : leftValue < rightValue) ? left.shift() : right.shift())
    }
    return combined.concat(left.length ? left : right)
  }
}

Puoi testarlo da solo trascinando lo snippet sopra nella console del browser, quindi provando:

var x = [2,76,23,545,67,-9,12];
x.mergeSort(); //[-9, 2, 12, 23, 67, 76, 545]
x.mergeSort(undefined, 'desc'); //[545, 76, 67, 23, 12, 2, -9]

Oppure ordina in base a un campo specifico in una matrice di oggetti:

var y = [
  {startTime: 100, value: 'cat'},
  {startTime: 5, value: 'dog'},
  {startTime: 23, value: 'fish'},
  {startTime: 288, value: 'pikachu'}
]
y.mergeSort('startTime');
y.mergeSort('startTime', 'desc');

0

Quindi avevo bisogno di un ordinamento stabile per la mia app React + Redux e la risposta di Vjeux qui mi ha aiutato. Tuttavia, la mia soluzione (generica) sembra diversa dalle altre che vedo finora, quindi la condivido nel caso in cui qualcun altro abbia un caso d'uso corrispondente:

  • Voglio davvero solo avere qualcosa di simile sort()all'API, dove posso passare una funzione di confronto.
  • A volte posso ordinare sul posto e talvolta i miei dati sono immutabili (perché Redux) e ho bisogno di una copia ordinata invece. Quindi ho bisogno di una funzione di ordinamento stabile per ogni caso d'uso.
  • ES2015.

La mia soluzione è creare un array digitato di indices, quindi utilizzare una funzione di confronto per ordinare questi indici in base all'array da ordinare. Quindi possiamo usare l'ordinamento indicesper ordinare l'array originale o creare una copia ordinata in un unico passaggio. Se questo crea confusione, pensaci in questo modo: dove normalmente passeresti una funzione di confronto come:

(a, b) => { 
  /* some way to compare a and b, returning -1, 0, or 1 */ 
};

Ora invece usi:

(i, j) => { 
  let a = arrayToBeSorted[i], b = arrayToBeSorted[j]; 
  /* some way to compare a and b, returning -1 or 1 */
  return i - j; // fallback when a == b
}

La velocità è buona; è fondamentalmente l'algoritmo di ordinamento incorporato, più due passaggi lineari alla fine e un ulteriore livello di sovraccarico indiretto del puntatore.

Felice di ricevere feedback su questo approccio. Ecco la mia completa implementazione di esso:

/**
 * - `array`: array to be sorted
 * - `comparator`: closure that expects indices `i` and `j`, and then
 *   compares `array[i]` to `array[j]` in some way. To force stability,
 *   end with `i - j` as the last "comparison".
 * 
 * Example:
 * ```
 *  let array = [{n: 1, s: "b"}, {n: 1, s: "a"}, {n:0, s: "a"}];
 *  const comparator = (i, j) => {
 *    const ni = array[i].n, nj = array[j].n;
 *    return ni < nj ? -1 :
 *      ni > nj ? 1 :
 *        i - j;
 *  };
 *  stableSortInPlace(array, comparator);
 *  // ==> [{n:0, s: "a"}, {n:1, s: "b"}, {n:1, s: "a"}]
 * ```
 */
function stableSortInPlace(array, comparator) {
  return sortFromIndices(array, findIndices(array, comparator));
}

function stableSortedCopy(array, comparator){
  let indices = findIndices(array, comparator);
  let sortedArray = [];
  for (let i = 0; i < array.length; i++){
    sortedArray.push(array[indices[i]]);
  }
  return sortedArray;
}

function findIndices(array, comparator){
  // Assumes we don't have to worry about sorting more than 
  // 4 billion elements; if you know the upper bounds of your
  // input you could replace it with a smaller typed array
  let indices = new Uint32Array(array.length);
  for (let i = 0; i < indices.length; i++) {
    indices[i] = i;
  }
  // after sorting, `indices[i]` gives the index from where
  // `array[i]` should take the value from, so to sort
  // move the value at at `array[indices[i]]` to `array[i]`
  return indices.sort(comparator);
}

// If I'm not mistaken this is O(2n) - each value is moved
// only once (not counting the vacancy temporaries), and 
// we also walk through the whole array once more to check
// for each cycle.
function sortFromIndices(array, indices) {
  // there might be multiple cycles, so we must
  // walk through the whole array to check.
  for (let k = 0; k < array.length; k++) {
    // advance until we find a value in
    // the "wrong" position
    if (k !== indices[k]) {
      // create vacancy to use "half-swaps" trick,
      // props to Andrei Alexandrescu
      let v0 = array[k];
      let i = k;
      let j = indices[k];
      while (j !== k) {
        // half-swap next value
        array[i] = array[j];
        // array[i] now contains the value it should have,
        // so we update indices[i] to reflect this
        indices[i] = i;
        // go to next index
        i = j;
        j = indices[j];
      }
      // put original array[k] back in
      // and update indices
      array[i] = v0;
      indices[i] = i;
    }
  }
  return array;
}

0

So che questo ha avuto molte risposte. Volevo solo portare avanti un post con un'implementazione rapida di TS per chiunque sia arrivato qui a cercarlo.

export function stableSort<T>( array: T[], compareFn: ( a: T, b: T ) => number ): T[] {
    const indices = array.map( ( x: T, i: number ) => ( { element: x, index: i } ) );

    return indices.sort( ( a, b ) => {
        const order = compareFn( a.element, b.element );
        return order === 0 ? a.index - b.index : order;
    } ).map( x => x.element );
}

Il metodo non viene più eseguito sul posto, come fa l'ordinamento nativo. Voglio anche sottolineare che non è il più efficiente. Aggiunge due cicli dell'ordine O (n). sebbene l'ordinamento stesso sia molto probabilmente O (n log (n)), quindi è inferiore.

Alcune delle soluzioni citate sono più performanti, pensato che potrebbe essere meno codice, anche utilizzando internal Array.prototype.sort.

(Per una soluzione Javascript, è sufficiente rimuovere tutti i tipi)


0

Secondo il blog dev v8 e caniuse.com Array.sort è già stabile come richiesto dalle specifiche nei browser moderni, quindi non è necessario eseguire il rollio della propria soluzione. L'unica eccezione che posso vedere è Edge, che dovrebbe presto passare a chrome e supportarlo anche lui.


0

function sort(data){
    var result=[];
    var array = data;
    const array2=data;
    const len=array2.length;
    for(var i=0;i<=len-1;i++){
    var min = Math.min.apply(Math,array)
    result.push(min);
    var index=array.indexOf(min)
    array.splice(index,1);
    }
    return result;
}   
sort([9,8,5,7,9,3,9,243,4,5,6,3,4,2,4,7,4,9,55,66,33,66]);


-1

L'ordinamento conteggio è più veloce dell'ordinamento di unione (si esegue in tempo O (n)) ed è inteso per l'uso su numeri interi.

Math.counting_sort = function (m) {
    var i
    var j
    var k
    var step
    var start
    var Output
    var hash
    k = m.length
    Output = new Array ()
    hash = new Array ()
    // start at lowest possible value of m
    start = 0
    step = 1
    // hash all values
    i = 0
    while ( i < k ) {
        var _m = m[i]
        hash [_m] = _m
        i = i + 1
    }
    i = 0
    j = start
    // find all elements within x
    while ( i < k ) {
        while ( j != hash[j] ) {
            j = j + step
        }
        Output [i] = j
        i = i + 1
        j = j + step
    }
    return Output
}

Esempio:

var uArray = new Array ()<br/>
var sArray = new Array ()<br/><br/>
uArray = [ 10,1,9,2,8,3,7,4,6,5 ]<br/>
sArray = Math.counting_sort ( uArray ) // returns a sorted array

12
Alcune cose da dire: 1. L'ordinamento conteggio funziona bene solo in uno spazio numerico denso. (Prova a ordinare l'array [1, 2e9, 1e9]) 2. Non scrivere per i cicli come i cicli while. 3. Non aggiungere cose a caso allo spazio dei nomi Math. 4. Potresti prendere in considerazione l'idea di fare amicizia con il punto e virgola.
Domi

Inoltre, nel caso in cui l'array abbia valori duplicati, verrà eseguito per sempre. Ad esempio, gli array [3, 1, 3]hash a [undefined, 1, undefined, 3]. Otteniamo due valori non indefiniti, mentre l'algoritmo si aspetta che ce ne siano tre.
MKPS
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.