L’algorimo SelectionSort scandisce tutti gli elementi dell’array a partire dall’ultimo elemento fino all’inizio e ad ogni iterazione esegue i seguenti passi:
L’algoritmo di SelectionSort è definito come segue:
Selection-Sort(A)
FOR i = length[A] DOWNTO 2 DO
max = Findmax(A,i)
"scambia A[max]con A[i]"
Findmax(A,x)
max = 1
FOR i = 2 to x DO
IF A[max] < A[i] THEN
max = i
return max
È facile osservare che, utilizzando le tecniche di calcolo del tempo di esecuzione, il tempo impiegato dalla funzione Findmax(A,x)
è proporzionale al numero di elementi tra l’indice 1 e l’indice x.
Infatti, il numero di esecuzioni della testa ciclo for è pari a x e ogni esecuzione del corpo del ciclo for impiega tempo costante.
Ne segue che ogni chiamata a Findmax(A,x)
impiega tempo
L’algoritmo Selection-Sort(A)
, eseguito su un array di n elementi, effettua n esecuzioni della testa del ciclo for. Ogni esecuzione del corpo effettua una chiamata Findmax(A,i)
che impiega tempo e uno scambio tra i valori di due posizioni dell’array, che impiega tempo costante.
Il tempo complessivo è dato, quindi, dalla formula .
L’algoritmo HeapSort:
Come vedremo, ogni operazione di ricostruzione di heap corrisponde ad una nuova ricerca del massimo.
In altre parole, HeapSort l’utilizzo della struttura dati heap garantisce che le ricerche del massimo possano essere effettuate in tempo sublineare.
Un albero binario è una struttura dati astratta definita come un insieme finito di nodi che:
Un nodo di un albero binario si dice nodo foglia (o solo foglia) se non ha figli (cioè se entrambi i sottoalberi di cui è radice sono vuoti).
Un nodo si dice nodo interno se ha almeno un figlio.
Un percorso dal nodo i al nodo j è la sequenza di archi che devono essere attraversati per raggiungere il nodo j dal nodo i.
In un albero binario la profondità di un nodo è la lunghezza del percorso dalla radice al nodo (cioè il numero di archi tra la radice e il nodo).
La profondità massima di un nodo all’interno di un albero è l’altezza dell’albero.
Il grado di un nodo è il numero di figli di quel nodo.
Un albero binario si dice pieno se:
Un albero binario si dice completo se:
Dalla definizione si evince che un albero binario pieno di altezza h è il più grande albero binario completo della stessa altezza h.
Un Albero Heap è un albero binario completo tale che per ogni nodo i: entrambi i nodi j e k figli di i hanno valore non maggiore (cioè, ≤) di i.
Uno Heap rappresenta un ordiamento parziale, infatti:
Ogni array A può essere visto come uno Albero Binario Completo in cui:
Ne consegue che i nodi interni dell’albero completo contengono la prima metà dei elementi dell’array, mentre le foglie contengono la seconda metà degli elementi.
Le seguenti funzioni implementano la corrispondenza tra array e albero binario completo:
SINISTRO(i)
return 2i
DESTRO(i)
return 2i+1
PADRE(i)
return |i/2|
Corrispondenza tra indici di un Array di elementi e nodi del corrispondente Albero Binario Completo.
Data la corrispondenza tra albero completo e array, possimo ora definire la nozione di array heap.
Un Albero Heap è un albero binario completo tale che per ogni nodo i: entrambi i entrambi i suoi nodi figli j e k hanno valore non maggiore del valore di i.
Un array A è uno Heap se:
A[i ] ≥ A[2i ] e A[i] ≥ A[2i+1]
Si noti che nei due esempi precedenti sia l’albero completo che l’array sono anche degli Heap.
Heapify(A,i)
: è l’operazione che ripristina la proprietà di Heap al (sotto)albero radicato in i, assumendo che i suoi sottoalberi sinistro e destro siano già degli Heap.
Heapify(A,i)
: dati due Heap H1 e H2 con radici SINISTRO(i)
e DESTRO(i)
e un elemento v in posizione i
:
se lo Heap H, con radice A[i] = v, vìola la proprietà di Heap (cioè se A[i] < A[SINISTRO
(i)] o A[i] < A[DESTRO
(i)]) allora:
SINISTRO(i)
o DESTRO(i).
Si noti che uno dei due sottoalberi rimane invariato, quindi è certamente uno Heap al termine dell’operazione.
Di seguito è riportato lo pseudocodice dell’algoritmo Heapify:
Heapify(A,i)
l = SINISTRO (i)
r = DESTRO(i)
IF l ⇐
heapsize[A] AND A[l] > A[i]
THEN maggiore = l
ELSE maggiore = i
IF r ⇐ heapsize[A] AND A[r] > A[maggiore]
THEN maggiore = r
IF maggiore ⇐ i
THEN "scambia A[i] e A[maggiore]"
Heapify(A,maggiore)
La variabile heapsize[A]
denota la dimensione corrente (numero di elementi) dello Heap.
Il controllo l ≤ heapsize[A] (r ≤ heapsize[A])
verifica, quindi, che l’elemento in posizione l (r
rispettivamente) sia effettivamente contenuto all’interno allo Heap.