PROGRAMMI DI RICERCA E ORDINAMENTO
Ricerca lineare. Abbiamo già visto l’algoritmo che esegue la ricerca
lineare di un elemento (detto chiave) in una lista o vettore, e il
corrispondente diagramma di flusso:
Siamo ora in grado di tradurlo in una funzione di nome ricercaLin,
che riceve in ingresso un vettore di interi, il numero dei suoi elementi
e la chiave da ricercare
ricercaLin(int elem[], int dim, int chiave)
{
int indice, trovato, i;
trovato = FALSO;
indice = -1;
i = 0;
while (i<dim && !trovato)
{
if (elem[i] == chiave)
{
trovato = VERO;
indice = i;
}
i++;
}
return(indice);
}
Osserviamo che il ciclo while è usato per accedere a ciascun
elemento del vettore, dal primo all’ultimo, fino a che si trova una
corrispondenza con la chiave desiderata.
In tale caso la variabile trovato viene impostata a VERO, il che fa
terminare il ciclo; altrimenti la ricerca continua fino a che s’incontra
la fine del vettore.
Per provare questa funzione, scriviamo una funzione pilota main()
che chiami ricercaLin e visualizzi il risultato da essa fornito.
Il programma completo è il seguente:
#include <stdio.h>
#define VERO 1
#define FALSO 0
#define N 10
void main(void)
{
int a[N] =
{5,10,22,32,45,67,73,98,99,101};
int voce, posto;
int ricercaLin(int [], int, int);
ricercaLin(int elem[], int
printf(“Scrivi la voce da cercare: “); dim, int chiave)
scanf(“%d”, &voce);
{
posto = ricercaLin(a, N, voce);
int indice, trovato, i;
if (posto > -1)
trovato = FALSO;
printf(“La voce è stata trovata al
indice = -1;
posto n° %d\n”, posto+1);
i = 0;
else
while (i<dim && !trovato)
printf(“La voce non è stata
{
trovata”);
if (elem[i] == chiave)
}
{
trovato = VERO;
indice = i;
}
i++;
}
return(indice);
}
Ricerca binaria. Ricordiamo che la ricerca binaria va eseguita su
un vettore ordinato, e che la sua strategia è la seguente:
a) si confronta la chiave con l’elemento centrale del vettore;
a1) se la chiave è uguale all’elemento centrale, la ricerca termina
con successo;
a2) se la chiave è maggiore dell’elemento centrale essa, se è
presente, si deve trovare nella parte superiore del vettore, e la
ricerca continua in essa;
a3) se la chiave è minore dell’elemento centrale essa, se è
presente, si deve trovare nella parte inferiore del vettore, e la
ricerca continua in essa.
Come abbiamo visto, lo pseudocodice che implementa questa strategia
è:
poni trovato = FALSO
poni indice = -1
poni indice inferiore = 0
poni indice superiore = dimensione del vettore -1
comincia con il primo elemento
while indice infer. <= indice super. AND non si è ancora
trovata la chiave
poni indice medio = media degli indici infer. e super.
if la chiave è uguale all’elemento di indice medio
la chiave è stata trovata
altrimenti, se la chiave è > dell’elemento di indice medio
poni indice inferiore = indice medio +1
altrimenti, se la chiave è < dell’elemento di indice medio
poni indice superiore = indice medio -1
fine dell’if
fine del while
fornisci il valore di indice;
Ed ecco la versione in C di questo pseudocodice:
ricercaBin(int elem[], int dim, int chiave)
{
int indice, trovato, inf, sup, medio;
indice = -1;
trovato = FALSO;
inf = 0;
sup = dim -1;
while (inf <= sup && !trovato)
{
medio = (int) ((inf + sup)/2);
if (chiave == elem[medio])
{
trovato = VERO;
indice = medio;
}
else if (chiave > elem[medio])
inf = medio + 1;
else
sup = medio - 1;
}
return(indice);
}
La precedente funzione ricercaBin andrà poi scritta dopo la
seguente funzione principale:
#include <stdio.h>
#define VERO 1
#define FALSO 0
#define N 10
void main(void)
{
int a[N] = {5,10,22,32,45,67,73,98,99,101};
int voce, posto;
int ricercaBin(int [], int, int);
printf(“Scrivi la voce da cercare: “);
scanf(“%d”, &voce);
posto = ricercaBin(a, N, voce);
if (posto > -1)
printf(“La voce è stata trovata al posto n° %d\n”,
posto+1);
else
printf(“La voce non è stata trovata”);
}
Osserviamo che questa funzione main() usata per la ricerca
binaria è la stessa già usata per la ricerca lineare (a parte,
ovviamente, il diverso nome di funzione usato nelle due istruzioni
che dichiarano e chiamano la funzione).
Ordinamento a bolle. Traduciamo ora in programmi C i vari algoritmi
di ordinamento già visti.
Cominciamo da quello a bolle, che si basa sul seguente algoritmo:
a) si confronta ciascun elemento, dal secondo all’ultimo, con il
precedente, e si scambiano se necessario;
b) si ripetono i confronti come prima, fermandosi la seconda volta al
penultimo elemento, la terza volta al terz’ultimo, ... , l’ultima volta al
secondo elemento.
Perciò, nel caso di un vettore con 10 elementi, si inizia confrontando
a[1] con a[0], poi a[2] con a[1], ... fino ad a[9] con
a[8], eseguendo gli scambi necessari.
A causa del modo con cui si effettuano i confronti e gli scambi, dopo la
prima scansione del vettore (i = 0) il valore più grande sarà
memorizzato nell’ultima componente del vettore (cioè in a[9]).
Per questa ragione la seconda scansione (i = 1), partendo sempre
dal confronto di a[1] con a[0], termina al confronto di a[8]
con a[7], lasciando in a[8] il secondo valore più grande.
In tal modo saranno eseguite in tutto 9 scansioni, al termine delle
quali il 9° valore in ordine di grandezza sarà memorizzato in a[1],
mentre il più piccolo si troverà in a[0].
Pertanto, nel caso di un vettore a n dimensioni, questo algoritmo
esegue n - 1 passate del vettore, mentre il numero di scambi
eseguiti dipende dal grado di “disordine” del vettore (è 0 se il
vettore è già ordinato).
Lo pseudo codice è quindi il seguente: (in questa fase, le istruzioni
indicate in colore non sono strettamente necessarie):
poni un contatore di scambi = 0
for l’indice i che va dal 1° elemento al penultimo
for l’indice j che va dal 2° elemento all’ultimo
if num[j] < num[j-1]
scambia num[j] con num[j-1]
incrementa il contatore di scambi
fine del for
fine del for
restituisci il contatore di scambi
Esso si traduce nella seguente funzione:
int ord_bolle(int a[], int numel)
{
int i, j, temp, scambi = 0;
for (i = 0; i < numel - 1; i++)
{
for (j = 1; j < numel - i; j++)
{
if (a[j] < a[j-1])
{
temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
scambi++;
}
}
}
return(scambi);
}
che va scritta dopo la seguente funzione principale:
#include <stdio.h>
#define N 10
void main(void)
{
int a[N] = {22,5,67,98,45,32,101,99,73,10};
int i, passi;
int ord_bolle(int [], int);
/* prototipo */
passi = ord_bolle(a, N);
/* chiamata */
printf("Il vettore ordinato in ordine crescente è:\n");
for (i = 0; i < N; ++i)
printf("%d ", a[i]);
printf(“\n Sono stati eseguiti %d scambi”, passi);
}
Bolle migliorato. All’algoritmo a bolle si può apportare un sostanziale
miglioramento modificandolo come segue:
a) si confronta ciascun elemento, dal secondo all’ultimo, con il
precedente, e si scambiano se necessario;
b’) si ripete il procedimento fino a quando si esegue una scansione
della lista senza effettuare alcuno scambio (perché in tale caso tutti
gli elementi sono nell’ordine corretto).
In tale modo il numero di passate da eseguire nel vettore non è più
fisso (e uguale a n-1), ma varia da 1 (nel caso il vettore sia già
ordinato) a n-1 (nel caso il vettore abbia il massimo grado di
disordine, ossia sia ordinato in senso decrescente).
La funzione che apporta questo miglioramento (e che va scritta dopo la
funzione principale appena vista) è la seguente:
int ord_bolle(int a[], int numel)
{
int i, j, temp, passi;
i = 0;
do
{
passi = 0;
for (j=1; j<numel-i; j++)
{
if (a[j] < a[j-1])
{
temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
passi++;
}
}
i++;
}
while (i < (numel-1) && passi != 0);
return (i);
}
Ordinamento per selezione
Vediamo ora il programma dell’ordinamento per selezione, che si basa
sul seguente algoritmo:
1. si seleziona inizialmente il più piccolo tra gli elementi a[1], ...
, a[n] (o uno dei più piccoli, se ve ne sono più uguali) e lo si
scambia con a[1];
2. tra gli elementi a[2], ... , a[n] del vettore così modificato si
seleziona il successivo elemento più piccolo e lo si scambia con
a[2], e così si prosegue.
3. Si continua così fino alla iterazione n-1-esima, dopo la quale tutto
il vettore a risulta ordinato.
Ricordiamo che lo pseudo codice di questo algoritmo è il seguente:
for ogni elemento, dal primo al penultimo
trova il più piccolo elemento, dal corrente all’ultimo:
ponendo il valore minimo uguale all’elemento corrente;
salvando l’indice dell’elemento corrente;
for ogni elemento, dal corrente +1 fino all’ultimo
if elemento[indice ciclo interno] < valore minimo
poni valore minimo = elemento[indice ciclo interno]
salva l’indice del nuovo valore minimo trovato
fine dell’if
fine del for
scambia il valore corrente con il nuovo valore minimo
fine del for
Questo pseudo codice esegue l’ordinamento tramite due cicli for
nidificati:
• Il ciclo esterno determina n – 1 passate attraverso il vettore; in
ognuna di esse alla variabile min è inizialmente assegnato il valore
num[i], dove i è la variabile contatore del ciclo for esterno.
Dato che i comincia da 0 e termina a n - 1, ogni elemento del
vettore, tranne l’ultimo, viene successivamente designato come
quello corrente.
• Il ciclo interno percorre ciclicamente gli elementi successivi a quello
corrente per selezionare il prossimo valore più piccolo, cominciando
dal valore dell’indice i+1 e continuando fino alla fine.
Quando viene trovato un nuovo minimo, il suo valore e la sua
posizione nel vettore sono memorizzati rispettivamente nelle variabili
min e indmin.
Una volta completato il ciclo interno, si effettua uno scambio solo se è
stato trovato un valore minore di quello nella posizione corrente.
Lo pseudo codice si traduce nella seguente funzione ordi_sele:
int ordi_sele(int num[], int dim)
{
int i, j, min, indmin, temp;
for (i = 0; i < (dim-1); i++)
{
min = num[i];
indmin = i;
for (j = i+1; j < dim; j++)
{
if (num[j] < min)
{
min = num[j];
indmin = j;
}
}
if (min < num[i])
{
temp = num[i];
num[i] = min;
num[indmin] = temp;
}
}
}
Essa si aspetta due argomenti:
• il vettore da ordinare (ossia l’indirizzo del suo primo elemento)
• il numero dei suoi elementi
e va scritta dopo la seguente funzione principale:
#include <stdio.h>
#define N 10
void main(void)
{
int a[N] = {22,5,67,98,45,32,101,99,73,10};
int i;
int ordi_sele(int [], int);
ordi_sele(a, N);
printf("Il vettore ordinato in ordine crescente è:\n");
for (i = 0; i < N; ++i)
printf("%d ", a[i]);
}
Si tratta della stessa funzione impiegata per l’ordinamento a bolle (a
parte l’ovvia differenza del nome della funzione chiamata).