Struttura dati Dizionario

Documenti analoghi
QuickSort Università degli Studi di Milano

ricerca di un elemento, verifica dell appartenenza di un elemento

Algoritmi e Strutture Dati. Capitolo 6 Il problema del dizionario

Algoritmi e Strutture Dati

Algoritmi e strutture dati

Esercitazione 6. Alberi binari di ricerca

Alberi di ricerca. Dizionari. Definizione. Alberi binari di ricerca (BST = binary search tree) Algoritmi e Strutture Dati

Alberi di ricerca. Alberi binari di ricerca. F. Damiani - Alg. & Lab. 04/05 (da C. Demetrescu et al - McGraw-Hill)

Un esempio: l ADT Dizionario (mutabile) Definizione. Alberi binari di ricerca (BST = binary search tree) search(chiave k) -> elem

Prova di Algoritmi e s.d. (1o anno) 17 Settembre TESTO e RISPOSTE

Per semplicità eliminiamo le ripetizioni nell'albero.

Algoritmi e strutture dati

Alberi binari di ricerca

Algoritmi e Strutture Dati

Alberi binari e alberi binari di ricerca

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati

PROGRAMMAZIONE II canale A-D luglio 2008 TRACCIA DI SOLUZIONE

LE STRUTTURE DATI DINAMICHE: GLI ALBERI. Cosimo Laneve

Organigramma Gerarchia. (Tree) Nessuna persona può avere più di un superiore Ogni persona può essere superiore di altre

Lezione 12 Tabelle Hash

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

Alberi binari di ricerca

Alberi binari e alberi binari di ricerca

Alberi binari di ricerca

Liste concatenate. Violetta Lonati

Alberi Binari di Ricerca

Alberi binari e alberi binari di ricerca

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

Alberi ed Alberi Binari

d. Cancellazione del valore 5 e. Inserimento del valore 1

Dato un insieme S di n elementi totalmente ordinato, l'algoritmo di ordinamento detto HeapSort ha le seguenti caratteristiche:

Dipartimento di Elettronica, Informazione e Bioingegneria API 2013/4

Albero binario. Alberi binari (introduzione) Terminologia. Alberi di ricerca binaria (BST)

Alberi. CORDA Informatica. A. Ferrari. Testi da. Marco Bernardo Edoardo Bontà. Dispense del Corso di. Algoritmi e Strutture Dati

L albero e un tipo astratto di dati usato per rappresentare relazioni gerarchiche.

Heap e code di priorità

Esercizi su ABR. Prof. E. Fachini - Intr. Alg.!1

Indici multilivello dinamici (B-alberi e B + -alberi) Alberi di ricerca - 1. Un esempio. Alberi di ricerca - 3. Alberi di ricerca - 2

In questa lezione Alberi binari di ricerca: la cancellazione

Il tipo astratto coda con priorità: specifiche sintattiche e semantiche. Realizzazioni.

Alberi e alberi binari I Un albero è un caso particolare di grafo

Esercizio 1. E vero che in un AVL il minimo si trova in una foglia o nel penultimo livello? FB = -1. livello 0 FB = -1. livello 1 FB = -1.

Alberi e alberi binari I Un albero è un caso particolare di grafo

Strutture dati Alberi binari

ADT Dizionario. Ordered search table. Supponiamo che sia definita una relazione d'ordine totale sulle chiavi del dizionario D:

Strutture dati dinamiche in C. Emilio Di Giacomo

Lezione 6: 12 Novembre 2013

Laboratorio di Algoritmi

Problemi di ordinamento

Code a priorità Una coda a priorità è una struttura dati astratta che permette di rappresentare un insieme di elementi su cui è definita una

Code con priorità. Moreno Marzolla Dip. di Scienze dell'informazione Università di Bologna.

Spesso sono definite anche le seguenti operazioni:

Tutoraggio Informatica Generale Inserimento e cancellazione in un ABR Counting Sort

Alberi binari (radicati e ordinati) della radice Il padre del nodo 5 e del nodo 3

Alberi Binari di Ricerca

Esercitazione 8. Corso di Tecniche di programmazione. Laurea in Ingegneria Informatica

Laboratorio di Informatica. Lezione 8: Liste e alberi

In questa lezione Strutture dati elementari: Pila Coda Loro uso nella costruzione di algoritmi.

ALBERI BINARI DI RICERCA

Un heap binario è un albero binario con le seguenti caratteristiche:

Questa soluzione va contemplata quando le lunghezze stimate dalle liste usate sono significativamente maggiori delle dimensioni di un elemento.

Note per la Lezione 4 Ugo Vaccaro

Laboratorio di algoritmi e strutture dati

ALGORITMI E STRUTTURE DATI

E12 Esercizi su Strutture dati dinamiche in C

Alberi binari di ricerca

Alberi. Definizioni principali

now is the for men all good come aid

Università degli Studi di L Aquila Facoltà di Scienze M.F.N. Corso di Laurea in Informatica. Modulo di Laboratorio di Algoritmi e Strutture Dati

Esercizi Capitolo 6 - Alberi binari di ricerca

Implementazione dell albero binario in linguaggio C++

Lezione 9 Alberi binari di ricerca

Algoritmi e Strutture di Dati

Tipi di dato e Strutture dati elementari

Alberi. Alberi: definizioni. Alberi Binari. Esercizi su alberi binari: metodi ricorsivi. Struttura dati per alberi generici. ASD-L - Luca Tesei

Esercitazioni di Fondamenti di Informatica - Lez. 7 20/11/2018

Multi-way search trees

Esercizi su alberi binari

Implementazione ADT: Alberi

ALBERI : introduzione SOMMARIO ALBERI ALBERI. ALBERI: introduzione ALBERI BINARI: introduzione VISITE RICORSIVE DI ALBERI

Un esempio di mutua ricorsione

Esercitazioni di Fondamenti di Informatica - Lez. 8 27/11/2018

Alberi. La definizione ricorsiva: Un albero radicato (non vuoto) è: o un singolo nodo o una radice connessa a un insieme di alberi.

23/10/2016. gli alberi. alcune definizioni. un esempio. LABORATORIO DI PROGRAMMAZIONE 2 Corso di laurea in matematica.

In questa lezione. Alberi binari: [CLRS09] cap. 12 per la visita inorder. visite e esercizi su alberi binari. Prof. E. Fachini - Intr. Alg.

Algoritmi e Strutture Dati

Esercizi parte 3. La classe ArrayBinTree dovra implementare, tra le altre, l operazione seguente: padre: dato un nodo, restituire l indice del padre.

Alberi Binari di Ricerca e Alberi Rosso-Neri

Laboratorio di Algoritmi e Strutture Dati. Code con Priorità

Algoritmi e Strutture di Dati

lezione 9 min-heap binario Heap e Alberi posizionali generali

Transcript:

Struttura dati Dizionario Un dizionario è : un insieme di coppie (elemento, chiave) Sul campo chiave è definita una relazione d'ordine totale Su cui definiamo le seguenti operazioni: insert(elem e, chiave k) aggiunge ad S una nuova coppia (e, k) delete(elem e, chiave k) cancella da S la coppia con chiave k search(chiave k) se la chiave k è presente in S restituisce l elemento e ad esso associato altrimenti restituisce null Le operazioni di inserimento e cancellazione rendono le struttura dati dinamica

Struttura dati Dizionario: Vettore non ordinato E' opportuno tenere traccia del numero n di elementi effettivamente presenti nel dizionario (dimensione logica dell array) Insert: O(1) (inserisco in fondo all array) Search: O(n) (devo scorrere l array) Delete: O(n) (cerco l'elemento da cancellare e copio l ultimo elemento nella posizione cancellata) E se usassimo una lista concatenata (ordinata o no)?

Albero binario Un albero binario radicato è una coppia T = (N, A) costituita da un insieme N di nodi ed un insieme A detti archi. In un albero: N N di coppie di nodi 1. Ogni nodo v (tranne la radice) ha un solo padre u tale che (u, v ) A.. Nodi con lo stesso padre sono detti fratelli 2. Un nodo u può avere zero o uno o due figli (nodi v tali che (u, v) A) 3. Un nodo senza figli è detto foglia, mentre i nodi che non sono né foglie né la radice sono detti nodi interni 4. La profondità (o livello) di un nodo è dato dal numero di archi che bisogna attraversare per raggiungerlo dalla radice 5. Altezza di un albero: massima profondità a cui si trova una foglia

Albero binario di ricerca Albero binario di ricerca (BST): albero binario in cui ogni nodo v contiene a) un valore chiave(v) del dizionario b) un campo padre parent(v) c) un campo figlio sinistro sin(v) d) un campo figlio destro des(v) Condizione: - Le chiavi nel sottoalbero radicato in sin(v) hanno valore chiave(v) - Le chiavi nel sottoalbero radicato in des(v) hanno valore > chiave(v) Tali proprietà inducono un ordinamento totale sulle chiavi del dizionario

Albero binario di ricerca! Albero binario di ricerca Albero binario non di ricerca: 47 49

Visita di un albero binario di ricerca Visita in ordine simmetrico dato un nodo v, elenco prima 1) il sotto-albero sinistro di v (in ordine simmetrico), poi 2) il nodo v, poi 3) il sotto-albero destro di v (in ordine simmetrico) algoritmo Visita_Inorder(node v) if (v null) then Visita_Inorder(sin(v)) stampa chiave(v) Visita_Inorder(des(v)) Visita_Inorder(root) visita tutti i nodi del BST una sola volta Complessità: T(n) = (n). Proprietà: Visita_Inorder visita i nodi del BST in ordine crescente rispetto alla chiave

Ricerca Search(chiave k): traccia un cammino nell albero partendo dalla radice: su ogni nodo, usa la proprietà del BST per decidere se proseguire nel sottoalbero sinistro o destro La complessità della procedura di ricerca considerata è T(n) = O(h), dove h è l altezza del BST.

Inserimento Insert(chiave k): inserisce nell'albero un nuovo nodo u con chiave k Cerca la chiave k nell albero, identificando così il nodo v che diventerà padre del nodo u; tale nodo v deve essere un nodo dal quale la ricerca di k non può proseguire (e quindi deve essere un nodo v che non ha sottoalbero sinistro e/o destro) Crea un nuovo nodo u con chiave = k Appendi u come figlio sinistro/destro di v in modo che sia mantenuta la proprietà di ordinamento totale La complessità della procedura considerata è T(n) = O(h), dove h è l altezza del BST

Predecessore Predecessor (chiave k): cerca il nodo v che ha la chiave che precede k nell'ordinamento delle chiavi (crescente) - Sia u il nodo che contiene la chiave k Caso 1. Esiste un sottoalbero sinistro di u. - cerchiamo il predecessore nel sottoalbero sinistro di radice u. Non può essere nel sottoalbero destro perché le chiavi sono > k - Cerchiamo il massimo nel sottoalbero del figlio sinistro si tratta del "nodo più a destra", individuabile scendendo sempre a destra in tale sottoalbero, fino a trovare un nodo senza figlio destro x u w v non qui

Predecessore: caso 2 Predecessor (chiave k): cerca il nodo v che ha la chiave che precede k nell'ordinamento delle chiavi (crescente) - Sia u il nodo che contiene la chiave k Caso 2. Non esiste un sottoalbero sinistro di u - cerchiamo il predecessore sul cammino verso la radice - è il primo nodo con chiave minore di k che si incontra sul cammino verso la radice - u deve essere nel sottoalbero destro di tale nodo v - Inoltre bisogna trovare quello più prossimo - Basta risalire da u verso i suoi antenati finché troviamo un nodo figlio destro di suo padre (u compreso): il padre è proprio il v cercato u v

Cancellazione Delete (chiave k): cerca la chiave k nell albero, identificando così il nodo u con chiave k 1. Se u è una foglia, essa viene rimossa aggiornando il parent(u) 2. Il nodo u da eliminare ha un unico figlio f Si elimina u e si attacca f a parent(u) 3. Il nodo u da eliminare ha due figli Si individua il predecessore (risp. successore) v di u Il predecessore (risp. successore) non ha figlio destro (risp. sinistro) Perché se avesse figlio destro non sarebbe predecessore, perché il figlio destro avrebbe chiave < k ma maggiore di quella di v Si copia il contenuto di v in u e si rimuove v

Albero binario di ricerca in C Definiamo in maniera astratta sia il contenuto di ciascun nodo che il corrispondente campo chiave: struct key { ; int value; typedef struct key key; struct item { ; key chiave; /* Qui eventuali altre informazioni */ typedef struct item item; Questo permette di cambiare chiave e informazioni ai nodi con poche modifiche

Operazioni sulle chiavi Nei file item.h e item.c inseriamo le definizioni appena viste e le funzione necessarie alle operazioni su item e key item new_item(key k){ item it; it.chiave = k; return it; La funzione new_item crea un nuovo item a partire da un oggetto key key new_key(int val){ key k; k.value = val; return k; La funzione new_key invece crea una variabile di tipo key a partire da un intero

Operazioni sulle chiavi - 2 Nel caso per creare nuove variabili di tipo item e key venga allocato spazio dinamicamente, esso va deallocato con opportune funzioni void destroy_item(item it){ destroy_key(it.chiave); /* in base a come è creato un item, potrebbe servire deallocare spazio*/ void destroy_key(key k){ /* non c'è nulla da deallocare in questa implementazione, ma in altre potrebbe servire*/ Serve anche ottenere la chiave di un item key get_key(item it){ return it.chiave;

Operazioni sulle chiavi - 3 Serve anche poter confrontare due chiavi: int cmp_key(key k1, key k2){ return k1.value - k2.value; void destroy_key(key k){ /* non c'è nulla da deallocare in questa implementazione, ma in altre potrebbe servire*/ Potrebbe servire anche stampare un item void print_key(key k){ printf("%d\n", k.value); void print_item(item it){ print_key(it.chiave); /* qui stampa di altre eventuali informazioni*/

Definiamo un albero binario di ricerca (BST) Definiamo ora la struttura nodo di un albero binario di ricerca: struct BST { ; item info; struct BST *sin, *des, *parent; typedef struct BST BST; Questa definizione rimane inalterata se cambiamo il contenuto di item, e lo stesso vale per le altre funzioni che implementeremo I tre campi puntatore, sin, des e parent puntano ai sottoalberi sinistro e destro, e al nodo padre rispettivamente

Creazione Creeremo un albero come puntatore nullo. Lo spazio necessario verrà allocato con l'inserimento. BST *createbst(void){ return NULL; Altre implementazioni potrebbero richiedere invece variazioni alla funzione createbst

Inserimento La funzione insertbst crea un nodo con la chiave k e lo introduce al posto giusto nell'albero. Viene restituita la nuova radice. BST *BSTinsert(BST *p, key k){ BST *q = malloc(sizeof(bst)); BST *r = p,*s = NULL; item it; it.chiave = k; if(!q) { fprintf(stderr,"errore di allocazione\n"); exit(-1); q->info = it; q->sin = q->des = NULL; while(r) { s = r; r=cmp_key(k,get_key(r->info))<0? r->sin:r->des; q->parent = s; if(!s) return q; /* q e' il primo nodo inserito */ if(cmp_key(k, get_key(s->info)) < 0) s->sin = q; else s->des = q; return p;

Inserimento: analisi BST *q = malloc(sizeof(bst));... it.chiave = k; if(!q){fprintf(stderr,"errore di allocazione\n");exit(- 1); q->info = it; q->sin = q->des = NULL; In primo luogo viene allocato il nuovo nodo puntato da q nella memoria dinamica. while(r){ s = r; r = cmp_key(k, get_key(r->info)) < 0? r->sin : r->des; r scende nell'albero scegliendo il ramo sinistro o destro a seconda dell'esito del confronto. s punta al padre di r. Si noti il confronto delle chiavi eseguito con la funzione cmp_key

Inserimento: analisi q->parent = s; if(!s) /* q e' il primo nodo inserito */ return q; if(cmp_key(k, get_key(s->info)) < 0) s->sin = q; else s->des = q; return p; Si collega il nodo puntato da q nel punto individuato da s. Se s è NULL, l'albero è vuoto e viene restituita la nuova radice q, altrimenti q viene agganciato come figlio sinistro o destro a seconda dell'esito del confronto fra le chiavi.

Ricerca /* funzione che cerca un nodo con chiave k nell'albero p*/ BST *BSTsearch(BST *p, key k) { if(!p cmp_key(k, get_key(p->info)) == 0) return p; return BSTsearch(cmp_key(k,get_key(p->info))<0? p->sin:p->des,k);

Predecessore BST *BSTmax(BST *p) { /* si assume p!= NULL */ for(;p->des; p = p->des); return p; ; BST *BSTpred(BST *q) { /* si assume q!= NULL */ BST *qq; if(q->sin) return BSTmax(q->sin); qq = q->parent; if(!qq) return q; /* q unico nodo*/ while(qq && q == qq->sin) { q = qq; qq = qq->parent; return qq;

Cancellazione BST * BSTdelete(BST *p, BST *q){ /* si assume q!= NULL */ BST *r, *s, *t = NULL; if(!q->sin!q->des) r = q; else r = BSTpred(q); /* Caso 3 :il predecessore non ha figlio destro*/ s= r->sin? r->sin: r->des;/* r->des usato per settare a NULL*/ if(s) /* se il predecessore ha figlio sinistro, si crea il ponte tra il figlio e il padre di s*/ s->parent = r->parent; if(!r->parent) t = s; /* se r è la radice*/ else /* si collega il figlio di r con il padre di r*/ if(r == r->parent->sin) r->parent->sin = s; else r->parent->des = s; if(r!= q)/* Caso 3: qui se se ci sono più informazioni nel nodo occorre copiarle*/ q->info.chiave = r->info.chiave; free(r); return t? t : (p!= r? p : NULL);

Cancellazione: analisi BST *r, *s, *t = NULL; r sarà uguale a q nei casi 1 e 2, sarà il successore di q nel caso 3. s sarà il figlio di r oppure NULL se r non ha figli. t sarà diverso da NULL solo se la radice dovrà essere modificata. if(!q->sin!q->des) r = q; else r = BSTpred(q); /* Caso 3 :il predecessore non ha figlio destro*/ s = r->sin? r->sin: r->des;/* r->right usato per settare a NULL*/ Viene determinato in quale dei tre casi siamo, e assegnati i valori corrispondenti a r e s if(s) s->parent = r->parent; if(!r->parent) t = s; /* se r è la radice*/ else /* si collega il figlio di r con il padre di r*/ if(r == r->parent->sin) r->parent->sin = s; else r->parent->des = s; Il nodo r viene estratto dall'albero, modi cando opportunamente il padre di r e s. Si gestisce il caso in cui r è la radice e il caso in cui s è NULL. return t? t : (p!= r? p : NULL); Ritorna t se la radice è da modificare, p se non è da modificare, NULL se q era l'unico nodo dell'albero.

Distruzione void destroybst(bst *p) /* si assume p!= NULL */ { while(p = BSTdelete(p,p));

Esercizi Esercizio 1. Scrivere un programma C che gestisca un albero binario di ricerca (BST). Il programma deve ciclare chiedendo all'utente quale tra 5 operazioni effettuare: 1) Creare un BST vuoto, 2) inserire una chiave (intero) nell'albero, 3) cancellare il nodo contenente una chiave, 4) cercare una chiave, 5) distruggere l'albero. Esercizio 2. Implementare un dizionario usando un vettore invece di un BST, e confrontare le prestazioni delle due implementazioni eseguendo n operazioni di inserimento, n operazioni di ricerca e n cancellazioni. Provare con n=500, 5000, 10000 e confrontare i tempi di esecuzione.

Esercizi Esercizio 3. Modificare il codice proposto per l'albero binario di ricerca in modo da avere delle stringhe come chiavi Esercizio 4. Scrivere una funzione che prenda in input un BST e ne esegua una stampa grafica rispettando la disposizione dei nodi dall'alto verso il basso e da sinistra verso destra