Algoritmi di ordinamento
In questa pagina 7
Problema: disporre gli elementi di una lista in ordine non decrescente. Tutti gli algoritmi qui sotto ordinano per confronti (<, <=), quindi funzionano con numeri, stringhe, tuple.
Proprietà da considerare:
- costo nel caso peggiore, migliore, medio (vedi Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →);
- sul posto (in place): usa solo memoria aggiuntiva;
- stabile: elementi uguali restano nell'ordine relativo di partenza (conta quando si ordina per una chiave, es. studenti per voto).
Selection sort
A ogni passo i si cerca il minimo di v[i:] e lo si scambia con v[i].
def selection_sort(v):
n = len(v)
for i in range(n - 1):
im = i
for j in range(i + 1, n):
if v[j] < v[im]:
im = j
v[i], v[im] = v[im], v[i]- Invariante:
v[:i]contiene gliielementi più piccoli, in ordine. - Confronti: sempre → in tutti i casi. Al più scambi.
- Sul posto, non stabile.
Insertion sort
Si scorre la lista; ogni elemento viene inserito al posto giusto nella parte iniziale già ordinata, spostando a destra i maggiori.
def insertion_sort(v):
for i in range(1, len(v)):
x = v[i]
j = i - 1
while j >= 0 and v[j] > x:
v[j + 1] = v[j]
j -= 1
v[j + 1] = x- Invariante:
v[:i]è ordinata (ma contiene gli elementi originali div[:i], non i più piccoli). - Caso peggiore (lista al contrario) ; caso migliore (già ordinata) : molto buono su liste quasi ordinate.
- Sul posto, stabile (grazie a
>stretto nelwhile).
Bubble sort
Si scorrono le coppie adiacenti scambiando quelle fuori ordine; dopo il passo k gli ultimi k elementi sono definitivi. Con un flag ci si ferma se in un passo non ci sono scambi.
def bubble_sort(v):
n = len(v)
for k in range(n - 1):
scambi = False
for j in range(n - 1 - k):
if v[j] > v[j + 1]:
v[j], v[j + 1] = v[j + 1], v[j]
scambi = True
if not scambi:
breaknel caso peggiore, se già ordinata; sul posto e stabile. Più lento di insertion sort in pratica.
Merge sort
Divide et impera (RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →): si divide la lista a metà, si ordinano ricorsivamente le due metà, si fondono le due metà ordinate (fusione con due indici, vedi Pattern algoritmici iterativiSchemi ricorrenti nei cicli: accumulatore, contatore, massimo e minimo, ricerca con uscita anticipata, verifica universale, filtro e trasformazione, finestra scorrevole, due indici, elaborazione di coppie.Pattern algoritmici iterativi →).
def merge_sort(v):
if len(v) <= 1:
return v
m = len(v) // 2
a = merge_sort(v[:m])
b = merge_sort(v[m:])
# fusione
out, i, j = [], 0, 0
while i < len(a) and j < len(b):
if a[i] <= b[j]: # <= rende l'algoritmo stabile
out.append(a[i]); i += 1
else:
out.append(b[j]); j += 1
out.extend(a[i:])
out.extend(b[j:])
return out- Costo → in tutti i casi: livelli di ricorsione, ciascuno con lavoro complessivo per le fusioni.
- Memoria aggiuntiva (non sul posto); stabile.
- Questa versione restituisce una lista nuova:
v = merge_sort(v).
Confronto
| Algoritmo | Peggiore | Migliore | Sul posto | Stabile |
|---|---|---|---|---|
| Selection sort | sì | no | ||
| Insertion sort | sì | sì | ||
| Bubble sort (con flag) | sì | sì | ||
| Merge sort | no | sì |
Nessun ordinamento per confronti può fare meglio di nel caso peggiore.
In Python: sort e sorted
Nella pratica si usa l'ordinamento predefinito (Timsort: , stabile, su dati già ordinati).
v.sort() # sul posto, restituisce None
w = sorted(v) # nuova lista
sorted(v, reverse=True) # decrescente
sorted(parole, key=str.lower) # ignorando maiuscole/minuscole
sorted(studenti, key=lambda s: (-s[1], s[0])) # voto decrescente, poi nomekey è una funzione applicata a ogni elemento: si confrontano i risultati. La stabilità permette ordinamenti a più livelli in passaggi successivi.
Errori tipici
v = v.sort()(vedi Liste in Pythonlist come sequenza mutabile (array dinamico); indici, slicing e assegnamento a fette; metodi e loro costo; aliasing, copia superficiale e profonda; list comprehension; liste annidate e matrici.Liste in Python →).- Nell'insertion sort, controllare
v[j] > xprima dij >= 0: conj = -1si leggev[-1](l'ultimo elemento) invece di fermarsi. - Merge sort che fonde senza aggiungere i residui (
extend) di una delle due metà.