Alberi binari di ricerca

Slides:



Advertisements
Presentazioni simili
Strutture dati per insiemi disgiunti
Advertisements

Ordinamento ottimo Ricerca
Algoritmi e Strutture Dati
Lez. 91 Universita' di Ferrara Facolta' di Scienze Matematiche, Fisiche e Naturali Laurea Specialistica in Informatica Algoritmi Avanzati Alberi di ricerca.
RB-alberi (Red-Black trees)
Hash Tables Indirizzamento diretto Tabelle Hash Risoluzioni di collisioni Indirizzamento aperto.
Breath-first search Visita in ampiezza di un grafo Algoritmo Esempio
Code a priorità (Heap) Definizione
Strutture dati elementari
Breath-first search Visita in ampiezza di un grafo Algoritmo Esempio
Code con priorità Ordinamento
Heap binomiali.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Stesso approccio.
Capitolo 3 Strutture dati elementari Algoritmi e Strutture Dati.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Ordinamenti ottimi.
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Capitolo 3 Strutture dati elementari Algoritmi e Strutture Dati.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Stesso approccio.
Capitolo 6 Alberi di ricerca Algoritmi e Strutture Dati.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Stesso approccio.
Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a
Esercizi su alberi binari
Alberi binari Definizione della struttura dati: struct tree { };
U V U V (a) |cfc|=2 prima e dopo (b) |cfc|=2 prima e |cfc|=1 dopo
1 Strutture dati avanzate Vedremo alcune strutture dati che permettono di eseguire in modo particolarmente efficiente un determinato insieme di operazioni.
Algoritmi e strutture Dati - Lezione 7
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 6 Interrogazioni.
Il problema del dizionario
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Capitolo 4 Ordinamento: Heapsort Algoritmi e Strutture Dati.
Algoritmi e Strutture Dati
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 6 Il problema.
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 6 Il problema.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Alberi AVL (Adelson-Velskii.
Interrogazioni su un albero binario di ricerca Search(S,k) – dato un insieme S ed un valore chiave k restituisce un puntatore x ad un elemento in S tale.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Alberi AVL (Adelson-Velskii.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 6 Rotazioni.
Algoritmi e Strutture Dati
Capitolo 4 Ordinamento Algoritmi e Strutture Dati.
Algoritmi e Strutture Dati Alberi Binari di Ricerca.
Algoritmi e Strutture Dati (Mod. A)
Algoritmi e Strutture Dati
Albero: insieme di punti chiamati NODI e linee chiamate EDGES
Algoritmi e Strutture Dati 20 aprile 2001
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati
Modello dati ALBERO Albero: Albero: insieme di punti chiamati NODI e linee chiamate EDGES EDGE: linea che unisce due nodi distinti Radice (root): in una.
Modello dati ALBERO Albero: Albero: insieme di punti chiamati NODI e linee chiamate EDGES EDGE: linea che unisce due nodi distinti Radice (root): in una.
Esercizi su alberi binari
Heap binari e HeapSort.
Heap binomiali Gli heap binomiali sono strutture dati su cui si possono eseguire efficientemente le operazioni: Make(H) : crea uno heap vuoto Insert(H,
Alberi di ricerca binari
Anche la RB-Delete ha due fasi: Nella prima viene tolto un nodo y avente uno dei sottoalberi vuoto sostituendolo con la radice dellaltro sottoalbero. Per.
Ispezione lineare La funzione hash h(k,i) si ottiene da una funzione hash ordinaria h'(k) ponendo L’esplorazione inizia dalla cella h(k,0) = h'(k) e continua.
RB-insert(T, z) // z.left = z.right = T.nil Insert(T, z) z.color = RED // z è rosso. Lunica violazione // possibile delle proprietà degli alberi // rosso-neri.
Per valutare la complessità ammortizzata scomponiamo ogni Union: nelle due FindSet e nella Link che la costituiscono e valuteremo la complessità in funzione.
Heap Ordinamento e code di priorità Ugo de Liguoro.
B trees.
Alberi binari Definizione della struttura dati: struct tree { };
Cammini minimi da un sorgente
Capitolo 6 Alberi di ricerca Algoritmi e Strutture Dati.
Alberi Alberi radicati : alberi liberi in cui un vertice è stato scelto come radice. Alberi liberi : grafi non orientati connessi e senza cicli. Alberi.
Capitolo 8 Code con priorità Algoritmi e Strutture Dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano.
Alberi rosso-neri Le operazioni sugli alberi binari di ricerca hanno complessità proporzionale all’altezza h dell’albero. Gli alberi rosso-neri sono alberi.
Algoritmi e strutture Dati - Lezione 7 1 Algoritmi di ordinamento ottimali L’algoritmo Merge-Sort ha complessità O(n log(n))  Algoritmo di ordinamento.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 6 Il problema.
Algoritmi e Strutture Dati HeapSort. Select Sort: intuizioni L’algoritmo Select-Sort  scandisce tutti gli elementi dell’array a partire dall’ultimo elemento.
Capitolo 6 Alberi di ricerca Algoritmi e Strutture Dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano.
Transcript della presentazione:

Alberi binari di ricerca Definizione Visita dell’albero inorder Ricerca Ricerca minimo, massimo e successore. Inserimento ed eliminazione di un nodo Problema del bilanciamento dell’albero

Albero binario Un albero binario è un albero dove ogni nodo ha al massimo due figli. Tutti i nodi tranne la radice ha un nodo padre. Le foglie dell’albero non hanno figli. In aggiunta, ogni nodo ha una chiave. Per rappresentare un albero binario si possono usare dei puntatori. Ogni nodo ha un puntatore al padre, al figlio sinistro e a quello destro. Inoltre, ad ogni nodo ha associato una chiave. Se un figlio o il padre è mancante, il relativo campo è uguale a NIL. 5 7 3 8 2 4

Albero binario di ricerca Definizione: Sia x un nodo di un albero binario di ricerca. Se y è un nodo appartenente al sottoalbero sinistro di x allora si ha key[y] ≤ key[x]. Se y è un nodo appartenente al sottoalbero destro di x allora si ha key[x] ≤ key[y]. 5 x 5 x 7 7 3 6 8 y 8 y 2 4 6 9 key[x] ≤ key[y] key[y] ≤ key[x]

Visita Inorder E’ un algoritmo ricorsivo! Richiede tempo Θ(n) con un albero di n nodi. INORDER-TREE-WALK(x) if x ≠ NIL then INORDER-TREE-WALK(left[x]) print key[x] INORDER-TREE-WALK(right[x]) Nel caso sia dato in ingresso un nodo x di un albero binario vengono stampati in ordine crescente le chiavi del sottoalbero che ha come radice x stesso. Questo è dovuto al fatto che la chiave del nodo x viene stampato dopo le chiavi del suo sottoalbero sinistro e prima di quelle del suo sottoalbero destro.

Visita Inorder root[T1] root[T2] 5 5 7 7 3 6 8 8 2 4 9 6 INORDER-TREE-WALK(root[T1]): 2, 3, 4, 5, 7, 8 INORDER-TREE-WALK(root[T2]): 5, 6, 6, 7, 8, 9

L’operazione di ricerca TREE-SEARCH(x,k) if x = NIL or k = key[x] then return x if k < key[x] then return TREE-SEARCH(left[x],k) else return TREE-SEARCH(right[x],k) Dato in ingresso il puntatore alla radice dell’albero e una chiave, l’algoritmo restituisce il nodo con chiave uguale a k oppure NIL se non esiste. L’algoritmo discende l’albero con una chiamata ricorsiva sfruttando le proprietà dell’albero binario di ricerca. Quindi non è necessario vedere tutti i nodi ma solo O(h), pari all’altezza h dell’albero.

L’operazione di ricerca INTERACTIVE-TREE-SEARCH(x,k) while x ≠ NIL or k ≠ key[x] do if k < key[x] then x ← left[x] else x ← right[x] return x Lo stesso algoritmo può essere implementato usando un ciclo while. In genere, è più efficiente. Dunque, il tempo per la ricerca di un nodo è pari a O(h), con all’altezza h dell’albero.

L’operazione di ricerca root[T] 9 5 15 4 7 16 12 2 6 8 11 13 19 1 3 18 21 TREE-SEARCH(root[T],18): 9 → 15 → 16 → 19 → 18 Trovato! TREE-SEARCH(root[T],10): 9 → 15 → 12 → 11 → NIL NON Trovato!

Minimo TREE-MINIMUM(x) while left[x] ≠ NIL do x ← left[x] return x Si utilizza le proprietà dell’albero binario di ricerca. Partendo dal nodo x si ha che: Se x ha un figlio sinistro allora il minimo è nel sottoalbero sinistro, poiché ogni nodo ys di questo è tale per cui key[ys] ≤ key[x]. Se x non ha un figlio sinistro allora ogni nodo yd nel sottoalbero destro è tale per cui key[x] ≤ key[yd]. Quindi, x è il minimo del sottoalbero con radice x.

Massimo L’algoritmo risulta simmetrico rispetto a TREE-MINIMUM(x). TREE-MAXIMUM(x) while right[x] ≠ NIL do x ← right[x] return x L’algoritmo risulta simmetrico rispetto a TREE-MINIMUM(x). Il tempo di esecuzione per trovare il massimo o il minimo in un albero binario di ricerca è al massimo pari all’altezza dell’albero, ossia O(h).

Minimo e Massimo root[T] 9 5 15 4 7 16 12 2 6 8 11 13 19 1 3 18 21 TREE-MINIMUM(root[T]): 9 → 5 → 4 → 2 → 1 Trovato! TREE-MAXIMUM(root[T]): 9 → 15 → 16 → 19 → 21 Trovato!

Successore Supponiamo che nel nostro albero ci siano solo chiavi distinte. Il successore di un nodo x è il nodo y con la chiave più piccola maggiore di key[x]: succ(key[x]) = min {y in T: key[x] < key[y]}. Sfruttando la struttura dell’albero binario di ricerca è possibile trovare il successore di un nodo senza dover confrontare le chiavi nei nodi. Nel seguente algoritmo si restituisce il successore di un nodo x oppure NIL se x è il nodo con la chiave più grande.

Successore TREE-SUCCESSOR(x) if right[x] ≠ NIL then return TREE-MINIMUM(right[x]) y ← p[x] while y ≠ NIL and x = right[y] do x ← y y ← p[y] return y // se y = NIL, x non ha un successore Se il nodo ha un figlio destro, il successore di x è il minimo del sottoalbero destro. Se il nodo non ha un figlio destro, si risale l’albero finché il nodo di provenienza sta a sinistra. In questo caso il nodo di partenza risulta essere il massimo del sottoalbero sinistro di y. Quindi, y è il suo successore.

Successore root[T] 9 5 15 4 7 16 12 2 6 8 11 13 19 1 3 18 21 TREE-SUCCESSOR(x con key[x] = 5): 5 → 7 → 6 Trovato nel sottoalbero destro. TREE-SUCCESSOR(x con key[x] = 8): 8 → 7 → 5 → 9 Trovato risalendo l’albero.

Successore Il tempo necessario per trovare il successore è pari a O(h), dove h è l’altezza dell’albero. Si effettua un cammino non più lungo della distanza massima tra la radice e una foglia. Il nodo y che precede x nella visita inorder è il nodo y con la chiave più grande minore di key[x] (key[y] ≤ key[x]). Per trovare il nodo che precede nella visita inorder, si utilizza un algoritmo simmetrico TREE-PREDECESSOR(x). TREE-PREDECESSOR(x) richiede tempo O(h) per motivi analoghi.

Inserimento e rimozione Quando si inserisce o si rimuove un elemento la struttura dell’albero cambia. L’albero modificato deve mantenere le proprietà di un albero binario di ricerca. La struttura dell’albero varia a seconda della sequenza di dati da inserire o rimuovere. L’inserimento risulta essere un’operazione immediata. La rimozione di un elemento è più complicata, proprio perché bisogna essere certi che l’albero rimanga un albero binario di ricerca.

Inserimento TREE-INSERT(T,z) y ← NIL // padre x ← root[T] // figlio while x ≠ NIL // while finché si raggiunge la posizione dove inserire z (x = NIL) do y ← x // memorizza il padre if key[z] < key [x] // scendi nel figlio giusto then x ← left[x] else x ← right[x] p[z] ← y // inserisci z come figlio di y if y = NIL // y = NIL albero vuoto then root[T] ← z else if key[z] < key [x] // y punta a z then left[y] ← z // z figlio sinistro key[z] < key [x] else right[y] ← z // z figlio destro key[x] ≤ key [z]

Inserimento Per inserire z si usano due puntatori y e x. Il puntatore a x scende l’albero , mentre y punta al padre di x. Nel ciclo while i due puntatori (x e y) scendono l’albero. x scende al figlio sinistro o destro a seconda dell’esito del confronto di key[z] con key[x]. Ci si ferma quando x = NIL e x occupa la posizione in cui z verrà inserito. Nelle linee 8-13 viene effettuato l’effettivo inserimento. Il tempo necessario per l’inserimento è O(h), ossia non più del cammino massimo tra la radice e una foglia (cioè h l’altezza).

Inserimento TREE-INSERT(T, z): z con key[z] = 14 root[T] 9 5 15 4 7 16 12 2 y 6 8 11 13 19 1 3 z 14 18 21

Inserimento TREE-INSERT(T, z) sequenza <5, 4, 7, 2, 6, 8> root[T] root[T] root[T] 5 5 5 4 4 7 root[T] root[T] root[T] 5 5 5 4 7 4 7 4 7 2 2 2 6 6 8

Inserimento TREE-INSERT(T, z) sequenza <5, 4, 7, 2, 6, 8> root[T] 5 4 7 2 6 8

Inserimento TREE-INSERT(T, z) sequenza <8, 7, 6, 4, 5, 2> root(T) 8 La struttura dell’albero risulta diversa a seconda della sequenza di inserimento! 7 6 4 2 5

Rimozione TREE-DELETE(T,z) if left[z] = NIL or right[z] = NIL then y ← z // z ha 0 o 1 figlio else y ← TREE-SUCCESSOR(z) // z ha due figli, trova succ(z) if left[y] ≠ NIL // x punta ad eventuale then x ← left[y] // unico figlio di y, altrimenti NIL else x ← right[z] if x ≠ NIL // se y ha il figlio then p[x] ← p[y] // taglia fuori y if p[y] = NIL then root[T] ← x // se y è la radice else if y = left[p[y]] // altrimenti then left[p[y]] ← x // completa eliminazione di y else right[p[y]] ← x if y ≠ z // se y è il successore then key[z] ← key[y] // copia y in z copia anche altri attributi di y in z return y

Rimozione Ci sono tre casi: Se z non ha figli, allora si modifica p[z] che punta non più a z, ma a NIL. Se z ha un unico figlio, allora si taglia fuori z dall’albero, facendo puntare p[z] all’unico figlio di z. Se z ha due figli, allora si individua il successore, ossia il minimo del suo sottoalbero destro. Il successore y ha nessun figlio o 1 figlio. Quindi y prende il posto di z, riconducendosi al caso 1 e 2. Alla fine i dati in y vengono copiati in z.

Rimozione TREE-DELETE(T, z) Caso 1: z senza figli. root(T) 9 5 15 4 7 16 12 2 6 8 11 13 19 1 3 z 14 18 21

Rimozione TREE-DELETE(T, z) Caso 2: z con 1 figlio. root(T) 9 5 15 z 4 7 16 12 2 6 8 11 13 19 1 3 14 18 21

Rimozione TREE-DELETE(T, z) Caso 3: z con 2 figli. root(T) 9 5 15 4 7 16 12 2 6 8 11 13 19 y 1 3 14 18 21

Rimozione TREE-DELETE(T, z) Caso 3: z con 2 figli. root(T) 9 5 15 4 7 y 16 13 2 6 8 11 14 19 1 3 18 21 z 12

Rimozione L’operazione di rimozione può richiede pochi passi, ma può succedere che TREE-SUCCESSOR(z) venga eseguito. TREE-SUCCESSOR(z) è O(h), dove h è l’altezza dell’albero. Quindi, la rimozione richiede tempo O(h). Riassumendo: TREE-INSERT() e TREE-DELETE() richiedono tempo O(h), dove h è l’altezza dell’albero, ossia il cammino massimo tra la radice e una foglia.

Alberi binari di ricerca Gli alberi di ricerca binari di ricerca sono strutture di dati sulle quali vengono realizzate molte delle operazioni definite sugli insiemi dinamici. Alcune operazioni sono: SEARCH, MINIMUM, MAXIMUM, PREDECESSOR, SUCCESSOR, INSERT e DELETE. Queste operazioni richiedono un tempo proporzionale all’altezza h dell’albero. E’ importante che l’albero sia bilanciato, in modo da non ricondurci ad una catena lineare. In questo caso, se si sono stati inseriti n nodi, si ha un tempo pari a Θ(n).

Alberi binari di ricerca Se l’albero è bilanciato, l’altezza dell’albero è pari a Θ(log(n)). Dunque, le operazioni sono eseguite nel caso peggiore con un tempo Θ(log(n)). Si può dimostrare che l’altezza di un albero binario di ricerca costruito in modo casuale è O(log(n)). Nella pratica, non si può garantire che gli alberi binari di ricerca siano costruiti in modo casuale, tuttavia ci sono varianti le cui prestazioni nel caso peggiore per le operazioni di base sono comunque buone (vedi gli RB-alberi).