Quando viene utilizzato ciascun algoritmo di ordinamento? [chiuso]


170

Quali sono i casi d'uso in cui un particolare algoritmo di ordinamento è preferito rispetto ad altri: unisci ordinamento vs QuickSort vs heapsort vs 'intro sort', ecc.?

Esiste una guida consigliata per utilizzarli in base alle dimensioni, al tipo di struttura dei dati, alla memoria e alla cache disponibili e alle prestazioni della CPU?


Una serie di animazioni per diversi tipi di dati e algoritmi è disponibile all'indirizzo <a href=" sorting-algorithms.com/"> sorting-algorithms.com </ a >
Chip Uni

2
Una guida come bigocheatsheet.com per queste cose sarebbe greaaaat
K - La tossicità in SO sta crescendo.

@ChipUni ecco il link fisso: toptal.com/developers/sorting-algorithms
eric,

2
Perché questa domanda è chiusa !?
Arvand,

Risposte:


316

Innanzitutto, una definizione, poiché è piuttosto importante: un ordinamento stabile è uno che garantisce di non riordinare gli elementi con chiavi identiche.

raccomandazioni:

Ordinamento rapido: quando non è necessario un ordinamento stabile e le prestazioni medie nel caso contano più delle prestazioni nel caso peggiore. Un ordinamento rapido è O (N log N) in media, O (N ^ 2) nel peggiore dei casi. Una buona implementazione utilizza l'archiviazione ausiliaria O (log N) sotto forma di spazio di stack per la ricorsione.

Unisci ordinamento: quando hai bisogno di un ordinamento stabile, O (N log N), si tratta della tua unica opzione. L'unico aspetto negativo è che utilizza lo spazio ausiliario O (N) e ha una costante leggermente più grande di un ordinamento rapido. Esistono alcuni tipi di unione sul posto, ma AFAIK non sono tutti stabili o peggiori di O (N log N). Anche la O (N log N) al suo posto ha una costante molto più grande del semplice vecchio ordinamento di fusione che sono più curiosità teoriche che algoritmi utili.

Ordinamento dell'heap: quando non hai bisogno di un ordinamento stabile e ti preoccupi di più delle prestazioni del caso peggiore rispetto alle prestazioni del caso medio. È garantito che sia O (N log N) e utilizza lo spazio ausiliario O (1), il che significa che non si esaurirà inaspettatamente l'heap o lo spazio dello stack su input molto grandi.

Introsort: si tratta di un ordinamento rapido che passa a un ordinamento heap dopo una certa profondità di ricorsione per aggirare il caso peggiore O (N ^ 2) dell'ordinamento rapido. È quasi sempre meglio di un semplice ordinamento rapido, poiché ottieni il caso medio di un ordinamento rapido, con prestazioni O (N log N) garantite. Probabilmente l'unica ragione per usare un ordinamento di heap invece di questo è nei sistemi fortemente limitati dalla memoria in cui lo spazio dello stack O (log N) è praticamente significativo.

Ordinamento di inserzione : quando N è garantito per essere piccolo, incluso come caso base di un ordinamento rapido o unisci ordine. Mentre questo è O (N ^ 2), ha una costante molto piccola ed è un ordinamento stabile.

Ordinamento a bolle, ordinamento per selezione : quando fai qualcosa di veloce e sporco e per qualche motivo non puoi semplicemente usare l'algoritmo di ordinamento della libreria standard. L'unico vantaggio che questi hanno rispetto all'ordinamento per inserzione è essere leggermente più semplice da implementare.


Tipi non di confronto: in alcune condizioni abbastanza limitate è possibile rompere la barriera O (N log N) e ordinare in O (N). Ecco alcuni casi in cui vale la pena provare:

Conteggio ordinamento: quando si ordinano numeri interi con un intervallo limitato.

Ordinamento radix: quando il registro (N) è significativamente maggiore di K, dove K è il numero di cifre radix.

Ordinamento bucket: quando è possibile garantire che l'input sia distribuito in modo approssimativamente uniforme.


1
Ricordo che l'heap sort ha anche un tempo di esecuzione molto prevedibile in quanto vi sono poche variazioni tra input diversi della stessa dimensione, ma questo è di minore interesse rispetto al suo costante limite di spazio. Trovo anche che l'inserimento sia il più semplice da implementare di n ^ 2 tipi, ma forse sono solo io. Infine, potresti anche menzionare l'ordinamento di Shell, che è quasi semplice da implementare come l'ordinamento di inserzione ma ha prestazioni migliori, anche se non è ancora registrato.
JaakkoK,

29
Non dimenticare Bogosort ! ;-)
Alex Brasetvik il

2
+1 Molto interessante. Ti andrebbe di spiegare come puoi "garantire ... approssimativamente distribuito uniformemente". per l'ordinamento della benna?
Sam Overton,

2
Perché l'introsort sarebbe sostanzialmente più lento dell'ordinamento rapido? L'unico sovraccarico è il conteggio della profondità di ricorsione, che dovrebbe essere trascurabile. Passa solo dopo che la ricorsione è molto più profonda di quanto dovrebbe essere in un buon caso di ordinamento rapido.
dsimcha,

2
Non puoi menzionare che il miglior caso di bubble sort è O (n)!
Tara,

33

quicksort è in genere il più veloce in media, ma ha alcuni comportamenti piuttosto cattivi nel caso peggiore. Quindi, se non devi garantire che non vi siano dati errati O(N^2), dovresti evitarli.

Merge-Sort utilizza memoria aggiuntiva, ma è particolarmente adatto per l'ordinamento esterno (ovvero file di grandi dimensioni che non rientrano nella memoria).

Heap-sort può essere ordinato sul posto e non presenta il comportamento quadratico nel peggiore dei casi, ma in media è più lento di quicksort nella maggior parte dei casi.

Laddove sono coinvolti solo numeri interi in un intervallo limitato, è possibile utilizzare un qualche tipo di ordinamento radix per renderlo molto veloce.

Nel 99% dei casi, starai bene con i tipi di libreria, che di solito si basano su quicksort.


6
+1: Per "Nel 99% dei casi, starai bene con i tipi di libreria, che di solito si basano su quicksort".
Jim G.

Il pivot casuale dà a Quicksort un runtime di O (nlogn) per tutti gli scopi pratici, senza bisogno di garanzie su dati errati. Non credo davvero che nessuno implementi un quicksort O (n ^ 2) per qualsiasi codice di produzione.
MAK,

2
MAK, tranne, diciamo, la libreria standard C qsort? ( google.com/codesearch/… ) - su cui si basa la maggior parte del "codice di produzione"
Eli Bendersky,

L'ordinamento delle librerie di solito non si basa su quicksort, perché non è stabile. Quasi tutte le lingue più alte (aspettatevi C) forniscono un ordinamento stabile. Nella maggior parte dei casi so che hai bisogno di un ordinamento stabile, o almeno deterministico.
12431234123412341234123


3

Ciò che i collegamenti forniti a confronti / animazioni non vengono considerati è quando la quantità di dati supera la memoria disponibile --- a quel punto il numero di passaggi sui dati, ovvero i costi I / O, domina il tempo di esecuzione. Se devi farlo, leggi "l'ordinamento esterno" che di solito copre le varianti di tipo merge e heap.

http://corte.si/posts/code/visualisingsorting/index.html e http://corte.si/posts/code/timsort/index.html hanno anche delle belle immagini che confrontano vari algoritmi di ordinamento.


0

@dsimcha ha scritto: Conteggio dell'ordinamento: quando si ordinano numeri interi con un intervallo limitato

Lo cambierei in:

Conteggio ordinamento: quando si ordinano numeri interi positivi (0 - Integer.MAX_VALUE-2 a causa del buco del piccione).

Puoi sempre ottenere i valori massimo e minimo come euristica di efficienza anche nel tempo lineare.
Inoltre è necessario almeno n spazio aggiuntivo per l'array intermedio ed è ovviamente stabile.

/**
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

(anche se in realtà consentirà a MAX_VALUE-2) vedere: Le matrici Java hanno una dimensione massima?

Spiegherei anche che la complessità dell'ordinamento di radix è O (wn) per n chiavi che sono numeri interi di parole w. A volte w viene presentato come una costante, che renderebbe l'ordinamento radix migliore (per n sufficientemente grande) rispetto ai migliori algoritmi di ordinamento basati sul confronto, che eseguono tutti i confronti O (n log n) per ordinare n chiavi. Tuttavia, in generale w non può essere considerato una costante: se tutte le n chiavi sono distinte, allora w deve essere almeno log n affinché una macchina ad accesso casuale sia in grado di memorizzarle in memoria, il che dà al massimo una complessità temporale O (n registro n). (da Wikipedia)

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.