Proprietà
- Stabilità: un algoritmo di ordinamento è stabile se preserva l’ordine iniziale tra due elementi con la stessa chiave (esempio: ordinamento per nome);
- Ordinamento sul posto (in place): non crea copie dell’input per generare la sequenza ordinata;
- Adattività: trae vantaggio dagli elementi già ordinati.
Selection sort
L’algoritmo seleziona di volta in volta il numero minore nella sequenza ancora da ordinare e lo sposta nella sequenza ordinata.
La sequenza viene quindi divisa in due parti: la sottosequenza ordinata, che occupa le prime posizioni dell’array, e la sottosequenza da ordinare, che costituisce la parte restante dell’array su cui ri-applicare l’algoritmo.
Il selection sort scorre sempre tutta la sequenza
Pseudocodice
SelectionSort(item A[], int n) {
for i = 1 to n - 1 do
int min = min(A, i, n)
A[i] <-> A[min]
}
int min(item A[], int i, int n) {
for j = i + 1 to n do
if A[j] < A[min] then
min = j
return min
}
Insertion sort
È un algoritmo efficiente per ordinare piccoli insiemi di elementi e si basa sul principio di ordinamento di una mano di carte da gioco: ogni carta viene presa dalla posizione in cui si trova per essere spostata dove sarà maggiore di tutte le carte precedenti (o all’inizio).
Pseudocodice
InsertionSort(item A[], int n) {
for i = 2 to n do
item temp = A[i]
int j = i
while j > 1 and A[j - 1] > temp do
A[j] = A[j - 1]
j = j - 1
A[j] = temp
}
int min(item A[], int i, int n) {
for j = i + 1 to n do
if A[j] < A[min] then
min = j
return min
}
Per ogni elemento si memorizza il valore A[i]
e, se necessario, si spostano tutti valori maggiori di A[i]
verso destra, creando uno spazio per inserire A[i]
nella posizione corretta.
Dal punto di vista della complessità l’insertion sort è migliore del selection sort nel caso ottimo e uguale nel caso pessimo.
Merge sort
Il merge sort, come la ricerca binaria, si basa sulla tecnica divide-et-impera, ossia la scomposizione del problema in problemi più piccoli:
- Divide: spezza virtualmente il vettore di n elementi in due sottovettori di n/2 elementi;
- Impera: esegue il merge sort ricorsivamente sui due sottovettori;
- Combina: unisce le due sequenze ordinate.
L’idea di base dell’algoritmo è di dividere la collezione in piccoli gruppi finché ogni gruppo è formato da un solo elemento (che è per definizione ordinato).
I gruppi sono quindi riuniti in modo tale che i loro elementi siano in ordine.
Pseudocodice
MergeSort(item A[], int first, int last) {
if first < last then
int mid = [(first+last)/2]
MergeSort(A, first, mid)
MergeSort(A, mid+1, last)
Merge(A, first, last, mid)
}
Merge(item A[], int first, int last, int mid) {
int i, j, k, h
i = first
j = mid+1
k = first
while i <= mid and j <= last do
if A[i] <= A[j] then
B[k] = A[i]
i = i+1
else
B[k] = A[j]
j = j+1
k = k+1
j = last
for h = mid downto i do
A[j] = A[h]
j = j-1
for j = first to k-1 do
A[j] = B[j]
}
- La funzione MergeSort viene chiamata ricorsivamente sulle metà sinistra e destra dell’array (i valori first, mid e last sono indici);
- La parte importante dell’algoritmo è nella funzione Merge, che riunisce le metà ordinate dell’array attraverso un array di appoggio.
Costo
Il costo della funzione Merge è
Quick sort
L’algoritmo seleziona un elemento p
di un vettore, chiamato pivot (di
solito il primo, l’ultimo o quello in mezzo). La scelta di p
non è necessariamente casuale. I valori inferiori al pivot vengono spostati a sinistra e quelli maggiori a destra.
I due sottovettori vengono ordinati chiamando ricorsivamente l’algoritmo, in modo che le due metà possano essere unite in quanto già ordinate al termine dell’esecuzione.
Pseudocodice
QuickSort(item A[], int lo, int hi) {
if lo < hi then
int j = pivot(A, lo,hi)
QuickSort(A,lo,j -1)
QuickSort(A,j+1,hi)
}
Scelta del pivot
int pivot(item A[], int lo, int hi) {
item pivot = A[lo]
int j = lo
for i = lo + 1 to hi do
if A[i] < pivot then
j=j+1
swap(A,i,j)
A[lo] = A[j]
A[j] = pivot
return j
}
Costo
- Caso pessimo: scelgo un brutto pivot; il vettore viene diviso in due sottovettori di dimensione
e ⇒ ; - Caso ottimo: scelgo un pivot ben posizionato; il vettore viene sempre diviso in due sottoproblemi di dimensione
⇒ .