Laboratorio di Programmazione Appunti sulla lezione 5: Algoritmi di ordinamento (cont.) Alessandra Raffaetà Università Ca’ Foscari Venezia Corso di Laurea in Informatica Bubblesort Idea: Due elementi adiacenti si scambiano posizione in modo che quello più piccolo si sposti verso l’inizio dell’array. □ Ad ogni passaggio l’elemento più piccolo si muove da destra a sinistra fluttuando come una bolla verso l’inizio dell’array. Ottimizzazione: Se in un passaggio non è stato effettuato nessuno scambio l’array è ordinato. Bubblesort void bubblesort(TipoElem v[ ], int dim) { int i = 0, j, ordinato; TipoElem temp; do { ordinato = 1; for (j = dim – 1; j > i; j --) if (maggiore(v[j - 1], v[j])) { /*elemento più piccolo galleggia verso l’alto*/ temp = v[j]; v[j] = v[j - 1]; v[j – 1] = temp; ordinato = 0; } i++; /* ordinato == 1 sse nessuno scambio è stato effettuato */ } while (i < dim – 1 && !ordinato); } Algoritmo di ordinamento: mergesort Algoritmo di tipo divide et impera. Divide: divide l’array v[p..u] in due sottoarray con metà elementi, v[p..med] e v[med+1..u] dove med = (p+u)/2. Impera: ordina ricorsivamente i sottoarray usando il mergesort se il sottoarray ha almeno due elementi, altrimenti il sottoarray è ordinato. Combina: fondi insieme i due sottoarray ordinati v[p..med] e v[med+1..u] al fine di ottenere un array ordinato v[p..u]. La procedura mergesort void mergesort (TipoElem v[ ], int iniziale, int finale) /*Ordina gli elementi del vettore v di indice compreso tra iniziale e finale */ { int med; if (iniziale < finale) { med = (iniziale + finale)/2; mergesort(v, iniziale, med); mergesort(v, med + 1, finale); merge(v, iniziale, med, finale); } } Esempio: le chiamate ricorsive di mergesort 1 8 3 2 0 1 2 3 1 8 3 2 0 1 2 3 5 7 4 5 4 9 6 7 5 7 4 1 8 3 2 0 1 2 3 5 4 5 7 4 9 6 7 4 9 6 7 5 1 8 3 2 5 7 4 9 0 1 2 3 4 5 6 7 La procedura merge Idea: Da due mazzetti ordinati di carte vogliamo farne un terzo mettendo di volta in volta la carta più piccola tra le due in cima ai due mazzetti. Questo si ripete fino a quando uno dei due mazzetti finisce. Le carte restanti sono aggiunte in fondo. void merge (TipoElem v [ ], int iniziale, int med, int finale) /*Fonde i due sottoarray ordinati di v da iniziale a med e da med+1 a finale in un unico sottoarray ordinato da iniziale a finale */ { TipoElem buf [DIM]; int primo,secondo, appoggio,i; primo = iniziale; secondo = med +1; appoggio = iniziale; /* vettore di appoggio */ La procedura merge (cont.) while (primo <= med && secondo <= finale) { if (minoreUguale(v [primo], v [secondo])) { buf [appoggio] = v [primo]; primo ++; } else { buf [appoggio] = v [secondo]; secondo ++; } appoggio++; } La procedura merge (cont.) if (secondo > finale) /* è finito prima il secondo sottoarray; copia da v in v stesso tutti gli elementi fino a med, cominciando dal fondo */ for (i= med; i>=primo; i--) { v[finale] = v[i]; finale --; } /* copia tutti gli elementi da iniziale a appoggio – 1 da buf a v */ for (i = iniziale; i < appoggio; i++) v[i] = buf[i]; } Come funziona la procedura merge Primo ciclo: Finché le due sottosequenze sono non vuote preleva l’elemento più piccolo in cima alle sottosequenze. Secondo ciclo: Disponi in posizione corretta gli elementi rimasti nella prima sottosequenza se non vuota. □ Osservazione: se la sottosequenza vuota è la prima non occorre fare alcuna operazione poiché gli elementi sono già in posizione corretta in base all’ordinamento. Terzo ciclo: Trasferisci gli elementi dal vettore di appoggio nella posizione corrispondente nel vettore di partenza. Esempio di calcolo della procedura merge 1 8 3 2 5 7 4 9 0 1 2 3 4 5 6 7 1 8 2 3 0 1 2 3 1 2 3 8 0 1 2 3 5 7 4 5 4 5 4 1 2 3 4 0 1 2 3 5 4 7 5 5 8 9 6 7 4 9 6 7 7 9 6 7 Counting sort Non si basa sul confronto ma … Assunzione: per ogni input < a1, …, aN >, ai є[0..M] Idea: □ Contare per ogni elemento dell’input x il numero di elementi minori di x. □ Usare questa informazione per posizionare l’elemento x direttamente nella sua posizione dell’array di output. Esempio: Se ci sono 17 elementi minori di x, allora x sarà posto nella posizione 17 nell’output (Ricordarsi che gli array in C hanno come indice iniziale 0). Counting sort void countingsort (int v[], int dim, int ris[]) { int i, occ[M+1]; for (i = 0; i < M+1; i++) /* inizializza occ */ occ[i] = 0; for (i = 0; i < dim; i++) occ[v[i]] ++; /* occ[i] == k sse i occorre k volte in v */ for (i = 1 ; i < M+1; i++) occ[i] += occ[i-1]; /*occ[i] contiene il num di elementi ≤ i in v */ for (i = dim-1; i>=0; i--) { ris[occ[v[i]] -1] = v[i]; /*dispone gli elementi di v in ris */ occ[v[i]]--; } } Esempio v occ occ occ ris ris 5 2 3 1 5 0 3 0 1 2 3 4 5 6 1 1 1 2 0 2 0 1 2 3 4 5 1 2 3 5 5 7 0 1 2 3 4 5 1 2 3 4 5 7 0 1 2 3 4 5 ? ? ? ? 3 ? ? 0 1 2 3 4 5 6 0 1 2 3 3 5 5 0 1 2 3 4 5 6 occ alla fine del secondo for. occ alla fine del terzo for. Prima iterazione del quarto ciclo. Array ordinato: fine quarto ciclo. Esercizio Dato un array v di dimensione n con interi nell’itervallo [0..M], utilizzare un’adeguata struttura dati in modo da poter determinare quanti elementi di v appartengono a un generico intervallo [a..b], utilizzando una semplice sottrazione.