Menu
Coddy logo textTech

Quick sort

Ultimo aggiornamento

Il quicksort è un algoritmo divide et impera che ordina attorno a un "pivot". Sceglie un elemento pivot, poi partiziona l'array in modo che tutto ciò che è più piccolo venga prima e tutto ciò che è più grande venga dopo, fissando così il pivot nella sua posizione ordinata finale. Poi procede ricorsivamente sulle partizioni sinistra e destra. Questa visualizzazione usa lo schema di Lomuto con l'ultimo elemento come pivot. Premi play per vedere il partizionamento e il posizionamento del pivot.

In pratica il quicksort è di solito l'ordinamento generico più veloce, grazie a un buon uso della cache e al partizionamento in loco, con una media di O(n log n). Il suo caso peggiore è O(n²) (ad es. un array già ordinato con una cattiva scelta del pivot), che buone strategie di scelta del pivot come la mediana di tre o la randomizzazione evitano.

Complessità temporale e spaziale

CasoComplessitàNote
Caso miglioreO(n log n)Partizioni bilanciate
Caso medioO(n log n)Ordine casuale
Caso peggioreO(n²)Pivot sempre sbilanciati
SpazioO(log n)Stack della ricorsione (partizione in loco)
StabileNoGli scambi della partizione riordinano gli elementi uguali

Passo dopo passo

PassoCosa succede
1Scegli un pivot (qui, l'ultimo elemento dell'intervallo).
2Partiziona: sposta tutti gli elementi più piccoli del pivot alla sua sinistra.
3Scambia il pivot sul confine: ora è nella sua posizione finale.
4Applica ricorsivamente il quicksort alla partizione sinistra.
5Applica ricorsivamente il quicksort alla partizione destra.

Esempio svolto

Ordinamento di [5, 2, 4, 1] con lo schema di Lomuto (ultimo elemento come pivot):

PassataArrayAzione
Inizio[5, 2, 4, 1]Partiziona l'intero intervallo; il pivot è 1 (ultimo elemento).
1[1, 2, 4, 5]Niente è più piccolo di 1, quindi scambia 1 all'indice 0; il pivot 1 ora è definitivo. Procedi ricorsivamente a destra su [2, 4, 5].
2[1, 2, 4, 5]Partiziona [2, 4, 5] con pivot 5; sia 2 sia 4 sono più piccoli, quindi 5 resta in fondo ed è definitivo. Procedi ricorsivamente a sinistra su [2, 4].
3[1, 2, 4, 5]Partiziona [2, 4] con pivot 4; 2 è più piccolo, quindi 4 resta dov'è ed è definitivo. 2 è un solo elemento, quindi è già ordinato.
Fine[1, 2, 4, 5]Ogni pivot è fissato al suo posto; l'array è ordinato.

Quando usare il quicksort

Usalo quandoEvitalo quando
Ti serve un ordinamento in memoria veloce e generico con fattori costanti piccoli.Ti serve un tempo O(n log n) garantito nel caso peggiore (usa heap sort o merge sort).
La memoria è poca: il partizionamento è in loco e richiede solo O(log n) di spazio sullo stack.Ti serve un ordinamento stabile che preservi l'ordine delle chiavi uguali.
I dati sono in ordine casuale o sconosciuto e usi un pivot casuale o la mediana di tre.L'input è già ordinato o quasi e il pivot è fisso, cosa che fa scattare O(n²).
Conta una buona località della cache, dato che il quicksort accede alla memoria in modo sequenziale.Stai ordinando una lista concatenata, dove il merge sort evita l'accesso casuale su cui si basa il quicksort.

Codice Quick Sort

Un'implementazione di Quick Sort pulita ed eseguibile in Python, JavaScript, Java, C++, C, Pseudocode. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.

Codice Quick Sort in Python

Python
1def quick_sort(a, low=0, high=None):2    if high is None:3        high = len(a) - 14    if low < high:5        p = partition(a, low, high)6        quick_sort(a, low, p - 1)7        quick_sort(a, p + 1, high)8    return a9
10
11def partition(a, low, high):12    # Lomuto partition: everything < pivot moves left of it13    pivot = a[high]14    i = low15    for j in range(low, high):16        if a[j] < pivot:17            a[i], a[j] = a[j], a[i]18            i += 119    a[i], a[high] = a[high], a[i]20    return i21
22
23nums = [10, 7, 8, 9, 1, 5]24print("Before:", nums)25quick_sort(nums)26print("After: ", nums)
Esegui questo codice nel playground Python

Domande frequenti sul quick sort

Qual è la complessità temporale del quicksort?
Il quicksort è in media O(n log n) ed è O(n log n) nel caso migliore, ma degrada a O(n²) nel caso peggiore, quando le partizioni sono sempre sbilanciate. I pivot casuali o con la mediana di tre rendono il caso peggiore molto improbabile.
Il quicksort è stabile?
No. La partizione standard in loco scambia elementi lontani tra loro, cosa che può cambiare l'ordine relativo delle chiavi uguali. Esistono varianti stabili, ma rinunciano al vantaggio del quicksort di lavorare in loco.
Perché il quicksort è spesso più veloce del merge sort?
Il quicksort partiziona in loco con un'ottima località della cache e senza buffer extra, quindi i suoi fattori costanti sono piccoli. Il merge sort ha lo stesso limite O(n log n), ma paga un buffer O(n) e più spostamenti di dati.
Quicksort o merge sort: quale usare?
Scegli il quicksort per ordinare in loco e velocemente array in memoria, dove i suoi fattori costanti piccoli di solito vincono. Scegli il merge sort quando ti serve un ordinamento stabile, un caso peggiore O(n log n) garantito, o quando ordini liste concatenate o dati esterni che non stanno in RAM.
Perché il quicksort diventa O(n²) su un array ordinato?
Con un pivot fisso, come il primo o l'ultimo elemento, un input già ordinato fa sì che ogni partizione stacchi un solo elemento, producendo n livelli di ricorsione invece di log n. Scegliere il pivot a caso o con la mediana di tre rompe questo schema e riporta il comportamento a O(n log n).
Qual è la differenza tra gli schemi di partizione di Lomuto e di Hoare?
Lo schema di Lomuto usa un solo indice che scorre da sinistra a destra ed è più semplice da scrivere, ed è per questo che questa visualizzazione lo usa. Lo schema di Hoare usa due puntatori che si muovono verso il centro e di solito fa meno scambi, quindi in pratica è più veloce, ma non colloca il pivot nella sua posizione finale durante il passo di partizione.
Illustrazione dei linguaggi di programmazione di Coddy

Padroneggia gli algoritmi con Coddy

INIZIA