1 / 37

RB-alberi (Red-Black trees)

RB-alberi (Red-Black trees). Proprietà degli RB-alberi Rotazioni Inserimento Rimozione. Proprietà degli RB-alberi. Un RB-albero (Red-black tree) è un albero binario di ricerca dove ogni nodo ha in aggiunta un campo per memorizzare il suo colore : RED (rosso) o BLACK (nero).

Download Presentation

RB-alberi (Red-Black trees)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. RB-alberi(Red-Black trees) Proprietà degli RB-alberi Rotazioni Inserimento Rimozione

  2. Proprietà degli RB-alberi • Un RB-albero (Red-black tree) è un albero binario di ricerca dove ogni nodo ha in aggiunta un campo per memorizzare il suo colore: RED (rosso) o BLACK (nero). • La colorazione avviene mediante regole precise, che assicurano che nessun cammino dalla radice ad una foglia risulti lungo più del doppio di qualsiasi altro. • Si ottiene così che l’albero è abbastanza bilanciato. • Ciascun nodo dell’albero contiene i campi color, key, left, right, e p.

  3. Proprietà degli RB-alberi • Un RB-albero (red-black tree) soddisfa le seguenti proprietà: • Ciascun nodo è rosso o nero. • Ciascuna foglia (NIL) è nera. • Se un nodo è rosso allora entrambi i suoi figli sono neri. • Ogni cammino da un nodo ad una foglia sua discendente contiene lo stesso numero di nodi neri. Dalla proprietà 4 si definisce la b-altezza di un nodo x. bh(x) = numero di nodi neri su un cammino da un nodo x, non incluso, ad una foglia sua discendente. Nota: Un nodo nero può avere figli rossi o neri. Tutti i nodi interni hanno due figli.

  4. Red-Black Albero root[T] 9 5 15 4 7 12 19 NIL NIL NIL 2 8 11 13 6 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL

  5. Altezza di RB-albero Un RB-albero con n nodi interni ha un’altezza di al più 2 lg(n+1). Dim.: Si ha che ogni sottoalbero con radice in x contiene almeno 2bh(x)-1 nodi interni. Dimostrazione per induzione: Se bh(x)=0, x è una foglia e si ha 20-1=1-1=0. Supponiamo vera per bh(x)=k-1, allora consideriamo un nodo x con bh(x)=k e k>0. x è un nodo interno (k>0) e ha due figli con b-altezzabh(x) o bh(x)-1. Allora i nodi interni nel sottoalbero con radice in x sono almeno (2bh(x)-1-1)+(2bh(x)-1-1)+1= 2bh(x)-1.

  6. Altezza di RB-albero Dim. (prosegue): Sia h l’altezza del RB-albero. Per la proprietà 3 almeno metà dei nodi sono neri. Quindi la b-altezza della radice è almeno h/2. Dunque, i nodi interni n sono almeno 2h/2-1. 2h/2-1 ≤ n h ≤ 2 lg(n+1).

  7. Operazioni su un RB-albero • Le operazioni di SEARCH, PREDECESSOR, MINIMUM e MAXIMUM sono quelle viste per un albero binario di ricerca. Possono essere eseguite in un RB-albero con un tempo pari a O(h) = O(lg(n)). • Le operazioni di INSERT e DELETE si complicano, poiché devono tener conto delle proprietà aggiuntive del RB-albero. Più precisamente si devono effettuare dei cambiamenti in modo da ripristinare le regole di colorazione dei nodi. • Tuttavia, RB-INSERT() e RB-DELETE() possono essere eseguite in tempo O(lg(n)).

  8. Rotazioni • Le rotazioni sono delle operazioni che cambiano la struttura dei puntatori di due nodi (padre e figlio). • E’ un’operazione locale dell’albero di ricerca che non modifica l’ordinamento delle chiavi. • Questa operazione sono utilizzate da INSERT e DELETE per ripristinare le proprietà violate degli RB-alberi mantenendo l’albero sempre un albero binario di ricerca.

  9. Rotazioni RIGHT-ROTATE(T,y) y x x y LEFT-ROTATE(T,x) c a a b b c a ≤ x ≤ b ≤ y ≤ c I due nodi interni x e y potrebbero essere ovunque nell’albero. Le lettere a, b e c rappresentano sottoalberi arbitrari. Un’operazione di rotazione mantiene l’ordinamento delle chiavi secondo la visita inorder.

  10. Rotazioni • LEFT-ROTATE(T,x) • y ← right[x] // inizializzazione di y • right[x] ← left[y] // inizio rotazione: sposta b • if left[y] ≠ NIL • then p[left[y]] ← x • p[y] ← p[x] // metti y al posto di x • if p[x] = NIL • then root[T] ← y // se x era la radice • else if x = left[p[x]] // modifica puntatore di p[x] • then left[p[x]] ← y • else right[p[x]] ← y • left[y] ← x // metti x a sinistra di y • p[x] ← y Si opera solo sui puntatori lasciando inalterati gli altri campi. RIGHT-ROTATE() è un procedura simmetrica. Entrambe le procedure sono eseguite in un tempo costante, quindi O(1).

  11. Inserimento • RB-INSERT(T,x) • TREE-INSERT(T,x) // ins. come albero b. di ricerca • color[x] ← RED // la proprietà 3 potrebbe essere violata p[x] = RED Si usa la procedura TREE-INSERT per inserire il nodo x nel RB-albero, visto che è un albero binario. Il nodo x viene colorato di rosso (i due figli NIL sono neri). Proprietà 1 e 2 sono soddisfatte: il nuovo nodo è rosso e ha due figli NIL neri. Proprietà 4 si mantiene il nuovo nodo è rosso e non aumenta la b-altezza fino ai suoi due figli neri. Potrebbe essere violata la proprietà 3, che dice che un nodo rosso non può avere figli rossi.

  12. Inserimento • while x ≠ root[T] and color[p[x]] = RED // finché prop. 3 violata • do if p[x] = left[p[p[x]]] // 3 casi: p[x] sinistra di p[p[x]] • then y ← right[p[p[x]]] • if color[y] = RED // caso 1: p[x] e fratello y RED • then color[p[x]] ← BLACK // y “zio” di x • color[y] ← BLACK • color[p[p[x]]] ← RED • x ← p[p[x]] Il ciclo while continua ad essere eseguita finché la proprietà 3 risulta invariata: x e p[x] sono entrambi RED. Lo scopo è quello di far “risalire” nell’albero questa violazione, mentre tutte le altre proprietà rimangono valide. Dopo ogni iterazione: o il puntatore x risale l’albero o viene eseguita qualche rotazione ed il ciclo termina.

  13. Inserimento Ci sono 3 casi + altri 3 simmetrici (la linea 4 li suddivide). Caso 1: y il fratello di p[x] è esso stesso RED. Quindi p[p[x]] è BLACK. Possibile nuova violazione tra p[p[x]] e suo padre: se entrambi RED! p[p[x]] p[p[x]] y p[x] y p[x] x x

  14. Inserimento • else if x = right[p[x]] // caso 2: zio y BLACK • then x ← p[x] // e x a destra di p[x] • LEFT-ROTATE(T,x) Caso 2: lo zio di x è BLACK e x è figlio destro di p[x]. Quindi, p[p[x]] è BLACK (unica violazione: tra x e p[x]!). bh(a) = bh(b) = bh(c) p[p[x]] Caso 3 x ← p[x] LEFT-ROTATE(T,x) y y p[x] x y a x c x c b b a

  15. Inserimento • color[p[x]] ← BLACK // caso 3: zio y BLACK • color[p[p[x]]] ← RED // e x a sinistra di p[x] • RIGHT-ROTATE(T,p[p[x]]) Caso 3: lo zio y di x è BLACK e x (RED) è figlio sinistro di p[x] (RED). Quindi, p[p[x]] è BLACK (unica violazione: tra x e p[x]!). color[p[x]] ← B color[p[p[x]]] ← R p[p[x]] p[p[x]] p[x] RIGHT-ROTATE(T,p[p[x]]) p[p[x]] p[x] p[x] y y x c a b c x x c y a b a b bh(a) = bh(b) = bh(c) = bh(y)

  16. Inserimento Il Caso 2 si trasforma nel Caso 3. La proprietà 4 si preserva. A questo punto (Caso 3) si deve effettuare alcuni cambiamenti di colore ed una rotazione destra che preserva la proprietà 4. Dopo il Caso 3 il ciclo while non è più ripetuto, in quanto p[x] è nero. A questo punto tutte le proprietà sono rispettate.

  17. Inserimento • else (altri 3 Casi simmetrici: scambia “right” e “left”) • color[root[T]] ← BLACK // nel caso x risalga fino alla radice La linea 17 è l’inizio degli altri 3 Casi simmetrici. Il codice risulta uguale, basta scambiare “right” con “left” e viceversa. L’ultima riga è importante: la radice risulta sempre nera. Se x non è la radice e p[x] risulta rosso, allora p[x] non è esso stesso la radice dell’albero e, quindi, esiste p[p[x]]. Questa condizione è fondamentale per il corretto funzionamento dell’algoritmo.

  18. Inserimento • RB-INSERT(T,x) • TREE-INSERT(T,x) // ins. come albero b. di ricerca • color[x] ← RED // la proprietà 3 violata? • while x ≠ root[T] and color[p[x]] = RED // finché prop. 3 violata • do if p[x] = left[p[p[x]]] // 3 casi: p[x] sinistra di p[p[x]] • then y ← right[p[p[x]]] • if color[y] = RED // caso 1: p[x] e fratello y RED • then color[p[x]] ← BLACK // y “zio” di x • color[y] ← BLACK • color[p[p[x]]] ← RED • x ← p[p[x]] • else if x = right[p[x]] // caso 2: zio y BLACK • then x ← p[x] // e x a destra di p[x] • LEFT-ROTATE(T,x) • color[p[x]] ← BLACK // caso 2: zio y BLACK • color[p[p[x]]] ← RED// e x a sinistra di p[x] • RIGHT-ROTATE(T,p[p[x]]) • else (altri 3 Casi simmetrici: scambia “right” e “left”) • color[root[T]] ← BLACK // nel caso x risalga fino alla radice

  19. Inserimento Analisi del tempo di esecuzione: • Dato che l’altezza di un RB-albero di n nodi è O(lg(n)), la chiamata TREE-INSERT() costa tempo O(lg(n)). • Il ciclo while è ripetuto solo se si esegue il Caso 1 con il conseguente spostamento verso la radice di x. • Per cui il numero massimo di volte che il ciclo while può essere ripetuto è O(lg(n)). • Ogni ciclo while è eseguito in tempo costante. • Quindi, RB-INSERT() impiega un tempo totale pari a O(lg(n)).

  20. Inserimento Un esempio pratico root[T] RB-INSERT(T,x) key[x] = 5 11 4 5 15 3 7 13 19 NIL NIL NIL NIL NIL 8 2 6 NIL NIL NIL NIL NIL NIL

  21. Inserimento Un esempio pratico root[T] Caso 1 11 4 15 3 7 13 19 y NIL NIL NIL NIL NIL 8 2 6 x NIL NIL NIL NIL NIL 5 Proprietà 3 violata NIL NIL

  22. Inserimento Un esempio pratico root[T] Caso 2 11 y 4 15 Proprietà 3 violata x 3 7 13 19 NIL NIL NIL NIL NIL 8 2 6 NIL NIL NIL NIL NIL 5 NIL NIL

  23. Inserimento Un esempio pratico root[T] Caso 3 Proprietà 3 violata 11 y 7 15 x 8 4 13 19 NIL NIL NIL NIL NIL NIL 3 6 NIL NIL 5 2 NIL NIL NIL NIL

  24. Inserimento Un esempio pratico root[T] Nota: l’albero è più bilanciato! 7 4 11 8 3 6 15 NIL NIL 5 2 13 19 NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL

  25. Rimozione La rimozione di un nodo da un RB-albero è un po’ più complicata dell’inserimento. Per semplificare il codice si fa uso di una sentinella nil[T] al posto di ogni foglia NIL. Il colore di nil[T] è BLACK, mentre gli altri campi (p, left, right, key) sono arbitrari. Tutti i puntatori a NIL sono sostituiti con puntatori a nil[T]. Il vantaggio è quello di poter trattare nil[T] come un nodo e poter quindi assegnare il valore p[nil[T]] necessario per il corretto funzionamento dell’algoritmo.

  26. Rimozione • RB-DELETE(T,z) • if left[z] = nil[T] o right[z] = nil[T] • 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[T] // x punta ad eventuale • then x ← left[y] // unico figlio di y, altrimenti a nil[T] • else x ← right[z] • p[x] ← p[y] // taglia fuori y • if p[y] = nil[T] • 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 • if color[y] = BLACK // chiama fixup se proprietà 4 • then RB-DELETE-FIXUP(T,x) // risulta violata • return y

  27. Inserimento Un esempio pratico root[T] RB-DELETE(T,x) con key[x] = 2 7 4 11 8 3 6 15 z = y 5 2 13 19 NIL

  28. Inserimento Un esempio pratico root[T] RB-DELETE(T,x) con key[x] = 2 7 4 11 8 3 6 15 5 13 19 NIL

  29. Rimozione La rimozione di un nodo da un RB-albero è una semplice modifica della procedura TREE-DELETE. Dopo aver rimosso il nodo y, si chiama RB-DELETE-FIXUP se color[y] è BLACK. Questo perché viene eliminato un nodo interno nero e la proprietà 4 sulla b-altezza non è più valida. In RB-DELETE-FIXUP(T,x) si considera che x abbia un colore nero “doppio”, cioè pari a 2. La proprietà 4 risulta cosi soddisfatta. Lo scopo è di spostare il colore nero di troppo verso la radice in modo da eliminarlo.

  30. Rimozione • RB-DELETE-FIXUP(T,x) • while x ≠ root[T] and color[x] = BLACK // spingi su BLACK extra verso radice • do if x = left[p[x]] // 4 casi se x = left[p[x]] • then w ← right[p[x]] • if color[w] = RED // caso 1 • then color[w] ← BLACK // fratello RED • color[p[x]] ← RED • LEFT-ROTATE(T, p[x]) • w ← right[p[x]] Caso 2, 3 o 4 Nero per color[w] Nero doppio Caso 1 D B x w B A D E w x C A e a b f C E Nero doppio c d a b c d e f

  31. Rimozione • if color[left[w]] = BLACK and color[right[w]] = BLACK • then color [w] ← RED // caso 2: w BLACK • x ← p[x] // figli di w BLACK Se il nuovo p[x] è RED il ciclo while termina. Per esempio, se si passa dal Caso 1 al Caso 2, il nuovo x (p[x]) è RED. Aggiungi colore nero Caso 2 nuovo x B B w x D D A A C C E E a b a b Nero doppio e e c d f c d f

  32. Rimozione • else if color [right[w]] = BLACK // caso 3: w BLACK, left[w] RED • then color[left[w]] ← BLACK // right[w] BLACK • color[w] ← RED • RIGHT-ROTATE(T,w) • w ←right[p[x]] Si è trasformato il Caso 3 nel Caso 4. B Caso 4 Caso 3 nuovo w x B C A w x D A c D a b C E Nero doppio a b d E Nero doppio e c d f e f

  33. Rimozione • color[w] ← color[p[x]] // caso 4: w BLACK • color[p[x]] ← BLACK // right[w] RED • color[p[x]] ← BLACK • LEFT-ROTATE(T,p[x]) • x ← root[T] // per terminare il ciclo e assicurarsi che root sia BLACK color ← cpx due neri Caso 4 cpx = color[p[x]] D B w w x B E D A A C e f C E a b nuovo x = root[T] c a b d Nero doppio e c d f

  34. Rimozione • RB-DELETE-FIXUP(T,x) • while x ≠ root[T] and color[x] = BLACK // spingi su BLACK extra verso radice • do if x = left[p[x]] // 4 casi se x = left[p[x]] • then w ← right[p[x]] • if color[w] = RED // caso 1: w RED • then color[w] ← BLACK // fratello RED • color[p[x]] ← RED • LEFT-ROTATE(T, p[x]) • w ← right[p[x]] • if color[left[w]] = BLACK and color[right[w]] = BLACK • then color [w] ← RED // caso 2: w BLACK • x ← p[x] // figli di w BLACK

  35. Rimozione • else if color [right[w]] = BLACK // caso 3: w BLACK • then color[left[w]] ← BLACK // right[w] BLACK • color[w] ← RED • RIGHT-ROTATE(T,w) • w ←right[p[x]] • color[w] ← color[p[x]] // caso 4: w BLACK • color[p[x]] ← BLACK // right[w] RED • color[p[x]] ← BLACK • LEFT-ROTATE(T,p[x]) • x ← root[T] // per terminare il ciclo e assicurarsi che root sia BLACK • else (analogo con “right” e “left” scambiati) • color[x] ← BLACK

  36. Rimozione In tutti i quattro casi (+ gli i 4 simmetrici) la proprietà 4 si mantiene dopo le modifiche, ossia il numero di nodi neri dalla radice a ognuno dei sottoalberi a, b, c ,d , e ed f rimane identico dopo la trasformazione. Tempo di esecuzione: Data l’altezza dell’albero è pari a O(lg(n)), la chiamata di RB-DELETE() senza considerare RB-DELETE-FIXUP() ha un costo di O(lg(n)) (vedi rimozione in un albero binario). In RB-DELETE-FIXUP(), il Caso 1 si riconduce ai Casi 2, 3 o 4 in tempo costante. I Casi 3, e 4 fanno terminare la procedura dopo l’esecuzione di un numero costante di passi.

  37. Rimozione Il Caso 2 è l’unico per il quale si potrebbe ripetere l’esecuzione del ciclo while. Questo può avvenire al più O(lg(n)) volte. Quindi la procedura RB-DELETE-FIXUP() impegna un tempo O(lg(n)) ed esegue al più tre rotazioni. Il tempo di esecuzione complessivo della procedura RB-DELETE() risulta O(lg(n)). Su un RB-albero le operazioni di SEARCH, PREDECESSOR, MINIMUM, MAXIMUM, INSERT e DELETE sono tutte O(lg(n)).

More Related