30.05.2013 Views

Appunti per il modulo di algoritmi e strutture dati - Sezione di ...

Appunti per il modulo di algoritmi e strutture dati - Sezione di ...

Appunti per il modulo di algoritmi e strutture dati - Sezione di ...

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

Se gli elementi dell’insieme sono mantenuti or<strong>di</strong>nati, è possib<strong>il</strong>e una ricerca più efficiente, detta ricerca<br />

binaria o logaritmica, sim<strong>il</strong>e a quella ut<strong>il</strong>izzata <strong>per</strong> l’albero binario <strong>di</strong> ricerca, che consiste nell’esaminare<br />

l’elemento centrale dell’array e, se questo è <strong>di</strong>verso dall’elemento da ricercare, andare a cercare nella<br />

prima metà dell’array, mentre, se è maggiore, nella seconda metà. L’algoritmo può essere definito nel<br />

modo seguente:<br />

int binSearch ( InfoType *A , InfoType x, int i=0 , int j=n -1) {<br />

if (i > j) return 0;<br />

int k=(i+j )/2;<br />

if (x == A[k]) return 1;<br />

if (x < A[k]) return binSearch (A, x, i, k -1);<br />

else return binSearch (A, x, k+1 , j)<br />

}<br />

È fac<strong>il</strong>e <strong>di</strong>mostrare <strong>per</strong> induzione la correttezza del programma. La relativa relazione <strong>di</strong> ricorrenza è :<br />

T (0) = a<br />

T (n) = T ( n<br />

2 ) + b n > 0<br />

e quin<strong>di</strong> T (n) è O(log n). Per quanto visto nel capitolo sui limiti inferiori, abbiamo che <strong>il</strong> problema<br />

della ricerca con <strong>algoritmi</strong> basati su confronti è Ω(log n). Quin<strong>di</strong> la ricerca binaria raggiunge tale limite<br />

inferiore. Naturalmente può non essere conveniente <strong>per</strong> implementare un insieme se ci sono frequenti<br />

inserzioni e cancellazioni, poiché l’array deve rimanere or<strong>di</strong>nato.<br />

11.2 Metodo hash<br />

Un metodo <strong>di</strong> ricerca in un insieme, non completamente basato su confronti, è <strong>il</strong> metodo detto hash<br />

(to hash = rimescolare). Questo metodo consiste nel definire una funzione hash h che associa ad ogni<br />

elemento dell’insieme una posizione nell’array (in<strong>di</strong>rizzo hash), cioè h: InfoType → in<strong>di</strong>ci. Questo vuol<br />

<strong>di</strong>re che, se l’elemento x è presente nell’insieme, esso deve trovarsi nella posizione corrispondente all’in<strong>di</strong>ce<br />

h(x). Una funzione hash genera tutti e soli gli in<strong>di</strong>ci dell’array, quin<strong>di</strong> h deve essere surgettiva. Quando h<br />

è anche iniettiva, la posizione degli elementi è essere sempre <strong>di</strong>versa <strong>per</strong> elementi <strong>di</strong>versi. Se h è iniettiva,<br />

abbiamo <strong>il</strong> cosiddetto accesso <strong>di</strong>retto:<br />

bool hashSearch ( InfoType *A , int n, InfoType x) {<br />

int i=h(x); if (A[i ]== x) return true ;<br />

else return false ;<br />

}<br />

La complessità della ricerca è ovviamente O(1), se <strong>il</strong> tempo <strong>per</strong> <strong>il</strong> calcolo della funzione hash è costante.<br />

Naturalmente l’inserimento <strong>di</strong> un elemento x deve essere effettuato in posizione h(x) e la cancellazione <strong>di</strong><br />

un elemento x inserirà un particolare valore in h(x), che in<strong>di</strong>ca che l’array non contiene elementi in quella<br />

posizione. Un esempio <strong>di</strong> funzione hash iniettiva, se gli elementi sono interi, è h(x) = x. Se non abbiamo<br />

alcuna informazione sulla struttura degli elementi dell’insieme, <strong>per</strong> progettare un accesso <strong>di</strong>retto bisogna<br />

prevedere tutte i possib<strong>il</strong>i elementi e questo, in generale, porta ad una <strong>di</strong>mensione dell’array che è <strong>di</strong><br />

molto su<strong>per</strong>iore al numero massimo n <strong>di</strong> elementi dell’insieme. Per esempio, se volessimo ut<strong>il</strong>izzare una<br />

ricerca ad accesso <strong>di</strong>retto <strong>per</strong> un insieme <strong>di</strong> parole italiane <strong>di</strong> lunghezza inferiore o uguale a 10, dovremmo<br />

associare ad ognuna delle possib<strong>il</strong>i sequenze <strong>di</strong> caratteri <strong>di</strong> lunghezza inferiore o uguale a 10 un in<strong>di</strong>ce<br />

<strong>di</strong>verso, e le sequenze <strong>di</strong> caratteri <strong>di</strong> lunghezza inferiore o uguale a 10 sono 26 10 + 26 9 + . . . 26, mentre <strong>il</strong><br />

numero <strong>di</strong> parole italiane formate al massimo da 10 caratteri è <strong>di</strong> molto inferiore.<br />

Se ci sono esigenze <strong>di</strong> ottimizzazione dell’occupazione <strong>di</strong> memoria tali da non <strong>per</strong>mettere l’uso <strong>di</strong> una<br />

funzione hash iniettiva, è necessario r<strong>il</strong>asciare <strong>il</strong> vincolo della iniettività. Se la <strong>di</strong>mensione dell’array è<br />

48

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!