Problema dell’Ordinamento
Problema dell’ordinamento
• Formulazione del problema
– Si vuole ordinare una lista di elementi secondo una
data proprietà P
• Esempio:
– Dato il vettore V = {3,1,7,9,12,5,3,4,2,10} di N = 10
numeri interi, lo si ordini in modo tale che sia
verificata la proprietà:
• P = { V[i]  V[i+1]
 i=1, .. N-1}
Problema dell’ordinamento
• Il problema può essere risolto utilizzando diversi
algoritmi:
–
–
–
–
Ordinamento per scambi (Bubble Sort)
Ordinamento per Inserzioni
Ordinamento per distribuzione e fusione (Merge Sort)
Ordinamento con doppio indice (Quick Sort)
Ordinamento per Scambi
• L’algoritmo Bubble Sort prevede:
– il confronto e l’eventuale scambio degli elementi del
vettore per coppie;
– il confronto va iterato su tutti gli elementi della
sottolista da ordinare in modo da portare l’elemento
massimo nell’ultima posizione;
– dopo aver portato l’elemento massimo della
sottolista nell’ultima posizione vengono ripetute
ciclicamente le operazioni precedenti in modo da
ordinare le restanti sottoliste.
Ordinamento per Scambi
• I iterazione
– Situazione iniziale 
3 1 7 9 12 5 3 4 2 10
– I passo 
3 1 7 9 12 5 3 4 2 10
Confronto e scambio
– II passo 
1 3 7 9 12 5 3 4 2 10
Confronto
– Dopo il passo N 
Sottolista ordinata
1 3 7 9 5 3 4 2 10 12
Ordinamento per Scambi
• Iterazione k+1
– Dopo la k-esima iterazione
Sottolista ordinata
k-1
k
Ordinamento per Scambi
• Problema dello scambio
– Lo scambio può avvenire correttamente solo se si
utilizza una variabile di comodo
Passo 3
V[i]
C
Passo 1
V[i+1]
Passo 2
Frammento di codice
C = V[i+1];
V[i+1] = V[i];
V[i] = C;
Ordinamento per Scambi
• Ordinamento con l’algoritmo Bubble Sort
// Ciclo di ordinamento della sottolista
for ( j = 0; j < N; j++ )
{
// Scansione interna al sottoarray per collocare nella
// ultima posizione l’elemento massimo
for ( k = 0; k <= j; k++ )
{
if ( lista[k] > lista[j] ) // if (!P)
{
// Scambia i valori
scambio = lista[k];
lista[k] = lista[k+1];
lista[k+1] = scambio;
}
}
}
Ordinamento per Scambi
• Ottimizzazione dell’algoritmo
– L’algoritmo può essere ottimizzato se si considera che:
• L’ultimo scambio in un ciclo determina la sottolista ordinata.
Infatti, se l’ultimo scambio in una iterazione è avvenuto alla
posizione k, vuol dire che la sottolista di elementi da k+1 ad N
è ordinata, quindi la successiva iterazione dovrà riguardare gli
elementi da 1 a k.
– L’algoritmo può terminare anticipatamente qualora in
una iterazione non vi sono stati scambi.
Ordinamento per Scambi
• Complessità
– Numero di operazioni di confronto
• n(n-1)/2
– Complessità O(n2)
Ordinamento per Inserzioni
• L’algoritmo di ordinamento per inserzioni prevede:
– si considera l’elemento a[i]
– la ricerca del primo elemento più piccolo (a[k]) di quello
selezionato all’interno della sottolista da ordinare;
– l’inserimento dell’elemento a[k] al posto di a[i] e si scalano di
una posizione in avanti tutti gli elementi della sottolista fino a
quello immediatamente precedente a[k];
– Si intera il procedimento con l’elemento a[i+1].
Ordinamento per Inserzioni
• I iterazione
– Situazione iniziale 
3 9 7 1 12 5 3 4 2 10
– I passo 
3 9 7 1 12 5 3 4 2 10
– II passo 
3 7 9 1 12 5 3 4 2 10
– III Passo
Elementi da scalare di una posizione
1 3 7 9 12 5 3 4 2 10
// Ciclo di ordinamento della sottolista
for ( j = 0; j < N; j++ )
for ( k = j+1; k < N; k++ )
if ( lista[k] < lista[j] )
{
// Salvataggio del valore V[k]
c = V[k];
// Shift degli elementi di una posizione
for(i=k-1; i=>j; i--)
V[i+1] = V[i];
// Inserzione nella posizione j del valore precedentemente salvato
V[j] = c;
}
Ordinamento per Inserzioni
• Complessità
– L’algoritmo richiede n inserzioni . L’inserzione
dell’j-mo elemento al posto k richiede k-1 confronti e
j-k spostamenti, cioè j-1 operazioni (+
l’inserimento). Il numero di operazioni è quindi
n(n-1)/2
– Complessità O(n2)
Ordinamento con doppio indice
(Quick Sort)
• L’algoritmo Quick Sort prevede l’utilizzo dell’
– algoritmo di separazione
– Utilizzo ricorsivo dell’algoritmo di separazione sulle due
sottoliste fino a ordinare tutti gli elementi
Ordinamento con doppio indice (Quick Sort)
• L’algoritmo Quick Sort prevede l’utilizzo dell’
– algoritmo di separazione: tale algoritmo, tramite lo
spostamento fisico di al più 3 elementi per volta, tende a
raggiungere una situazione per cui:
• sia identificato un elemento di sparazione x
• siano identificate due sottoliste, una a sinistra ed una a
destra di x tali che:
– gli elementi della prima siano non maggiori di x
– gli elementi della seconda siano non minori di x
– l’algoritmo appena descritto viene ripetuto ricorsivamente
sulle due sottoliste fino a ordinare tutti gli elementi
Algoritmo di separazione
• Si partiziona l’array in modo che
– Si sceglie come elemento di partizione un
elemento (es. l’ultimo della lista) a[m]
– Si usa a[m] come elemento di separazione in
modo che
a[i]  a[m] per ogni i < m
a[i]  a[m] per ogni i > m
Algoritmo di Separazione
s
d c
9 1 6 4 5
8 2 3 0 6
s
0 1 6 4 5
d c
8 2 3 6 9
d c s
0 1 6 4 5
3 2 6 8 9
0 1 6 4 5
3 2 6 8 9
L1
L2
A[i]  A[8]
A[i]  A[8]
0 1 6 4 5
3 2 6 8 9
d s
c
0 1 6 4 5
3 2
d c
s
0 1 2 4 5
3 6
c
4 5
3 6
s
c
4 5
3
3 5
4
5
4
Ordinamento con doppio indice
• Passi dell’algoritmo di separazione
– si parta da una lista a[1]...a[n]
– si assume come elemento di separazione un qualsiasi
elemento dell’array x in posizione c (ad esempio
l’ultimo)
– si cerca il primo elemento da sinistra maggiore di x.
Supponiamo sia in posizione s (se tale elemento non
esiste si pone s = n+1)
– si cerca il primo elemento da destra minore di x.
Supponiamo sia in posizione d (se tale elemento non
esiste si pone d = 0)
Ordinamento con doppio indice
– si ordinano gli elementi trovati in modo che x
risulti al centro, a[d] vada alla sua sinistra e a[s]
vada alla sua destra. Nel caso s = n+1 o d = 0
viene effettuato l’ordinamento dei soli due
elementi trovati: a[c] e a[d] oppure a[s] e a[c]
– in questo momento risultano
• a[1]...a[s] formata da elementi non maggiori di x
• a[d]...a[n] formata da elementi non minori di x
Ordinamento con doppio indice
– sia c la nuova posizione dell’elemento di
separazione x, ottenuta tramite lo spostamento
precedente
– mentre s < d si riparte dal punto 3
dell’algoritmo, cercando da sinistra e da destra
a partire da s e da d
Int separa(int a[], int left, int right)
{
Int s = left –1;
Int d = right;
Int p = a[right];
Int temp;
for(;;) {
while (a[++s] >p && (s<right)) ;
while (p < a[- -d])
if (d = = left)
break;
if (s>=d)
break;
temp=a[s];
a[s]=a[d];
a[d]=temp;
}
temp=a[s];
a[s]=a[right];
a[right]=temp;
return s;
}
La routine quicksort
void quicksort(int lista[], int a, int z)
{
int cf = 0;
if(z>a)
{
cf = separa(lista, a, z);
quicksort(lista, a, cf-1);
quicksort(lista, cf+1, z);
}
}
Ordinamento per Distribuzione e Fusione
• L’algoritmo Merge Sort prevede 3 passi:
– Suddividi l’array in duw sottoarray
– ordina i sottoarray;
– esegui la fusione dei sottoarray ordinanti in un unico
vettore.
• Problema della fusione di due liste ordinate:
– Siano V1 e V2 due vettori ordinati di dimensione
rispettivamente N1 ed N2. Siano i e j gli indici
rispettivamente su V1 e V2. Il vettore fusione V si
otterrà con la seguente procedura:
• Si confrontano il primo elemento di V1 ed il primo elemento
di V2. L’elemento minore verra copiato in V, quindi verranno
incrementati gli indici k su V ed i su V1 se l’elemento minore
è stato ottenuto da V1, j su V2 se l’elemento minore è stato
ottenuto da V2;
• Si iterano i confronti finché non si raggiunge il termine di V1
o V2, quindi si completa il vettore fusione V con gli elementi
del vettore per il quale non si è giunti ancora al termine.
Fusione di due vettori ordinati
1
2 1
9 7
10 3
4 1
2
3
2 1
4 3
2 1
Nucleo della funzione merge:
void merge(V1,V2) {
i = j = 0;
// Ciclo di fusine delle due liste
while ((i < N1) && (j < N2))
{
// Confronto tra gli elementi di V1 e V2
if(V1[i] < V2[j])
// L’elemento copiato in V proviene da V1
V[k++] = V1[i++];
else
// L’elemento da inserire in V proviene da V2
V[k++] = V2[j++];
}
// Completamento del vettore V
if ( i < N1 )
// Il vettore V deve essere completato con gli elementi di V1
while (i < N1)
V[k++] = V1[i++];
else
// Il vettore V deve essere completato con gli elementi di V2
while (j < N2)
V[k++] = V2[j++];
}
}
Problema della distribuzione - Descrizione della
procedura sort:
– La suddivisione di una lista in sottoliste ordinate avviene in
questo modo:
• se la lista contiene più di due elementi viene calcolato il
punto medio della lista, vengono create due sottoliste a
partire da esso ed infine, viene richiamata ricorsivamente la
funzione di sort per entrambe le sottoliste create;
• se la lista è costituita da due elementi (condizione di uscita
dalla ricorsione), si procede con un confronto ed eventuale
scambio dei due elementi; questo garantisce la restituzione di
sottoliste ordinate.
Nucleo della procedura sort:
Void sort(V) {
if(Lunghezza > 2)
{
// La procedura separa divide la lista V in due
vettori V1 e V2
separa(V,V1,V2);
// Chiamate ricorsive su V1 e V2
sort(V1);
sort(V2);
// fusione delle liste
merge(V1,V2);
}
else
// Raggiunta la condizione di uscita dalla ricorsione
if(V[1] > V[2])
// Effettua lo scambio
{
scambio = V[1];
V[1] = V[2];
V[2] = V[1];
}
}
Merge Sort
Void mergesort(listaCompleta)
{
sort(listacompleta);
}
Scarica

ordinamento1