Strutture dati avanzate Vedremo alcune strutture dati che permettono di eseguire in modo efficiente un determinato repertorio di operazioni: - B-alberi.

Slides:



Advertisements
Presentazioni simili
Strutture dati per insiemi disgiunti
Advertisements

Alberi binari Definizione Sottoalberi Padre, figli
Estendere i linguaggi: i tipi di dato astratti
Liste di Interi Esercitazione. Liste Concatenate Tipo di dato utile per memorizzare sequenze di elementi di dimensioni variabile Definizione tipicamente.
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)
Strutture dati elementari
Alberi binari di ricerca
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.
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.
Esercizi su alberi binari
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
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 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 Alberi Binari di Ricerca.
Algoritmi e Strutture Dati (Mod. A)
Algoritmi e Strutture Dati
Algoritmi e Strutture Dati 20 aprile 2001
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,
Tavole dinamiche Spesso non si sa a priori quanta memoria serve per memorizzare dei dati in un array, in una tavola hash, in un heap, ecc. Può capitare.
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.
Esercizio 10.* Un cassiere vuole dare un resto di n centesimi di euro usando il minimo numero di monete. a) Descrivere un algoritmo goloso per fare ciò.
Strutture dati per insiemi disgiunti
Radix-Sort(A,d) // A[i] = cd...c2c1
Vedremo in seguito che (n log n) è un limite stretto per il problema dellordinamento. Per ora ci limitiamo a dimostrare che: La complessità nel caso pessimo.
Cerchiamo di rispondere alla seconda domanda 2)La soluzione trovata con lalgoritmo goloso è ottima o esistono anche soluzioni con più di quattro attività?
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.
La complessità media O(n log n) di Quick-Sort vale soltanto se tutte le permutazioni dell’array in ingresso sono ugualmente probabili. In molte applicazioni.
B trees.
Alberi CORDA – Informatica A. Ferrari Testi da
Valutare la difficoltà dei problemi
alberi completamente sbilanciati
Implementazione di dizionari Problema del dizionario dinamico Scegliere una struttura dati in cui memorizzare dei record con un campo key e alcuni altri.
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.
Risoluzione delle collisioni con indirizzamento aperto Con la tecnica di indirizzamento aperto tutti gli elementi stanno nella tavola. La funzione hash.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl Capitolo 6 Rotazioni.
Algoritmi e Strutture Dati
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.
1 Analisi ammortizzata Si considera il tempo richiesto per eseguire, nel caso pessimo, una intera sequenza di operazioni. Se le operazioni costose sono.
Camil Demetrescu, Irene Finocchi, Giuseppe F. ItalianoAlgoritmi e strutture dati Copyright © The McGraw - Hill Companies, srl 1 Soluzione esercizio.
1 Ordinamento (Sorting) Input: Sequenza di n numeri Output: Permutazione π = tale che: a i 1  a i 2  ……  a i n Continuiamo a discutere il problema dell’ordinamento:
Codici prefissi Un codice prefisso è un codice in cui nessuna parola codice è prefisso (parte iniziale) di un’altra Ogni codice a lunghezza fissa è ovviamente.
1 Un esempio con iteratore: le liste ordinate di interi.
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 1 Progettare algoritmi.
Come aumentare gli alberi
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.
Prof.ssa Rossella Petreschi Lezione del 17 /10/2014 del Corso di Algoritmica Lezione n°5.
Prof.ssa Rossella Petreschi Lezione del 15 /10/2012 del Corso di Algoritmica B-alberi Lezione n°5.
Transcript della presentazione:

Strutture dati avanzate Vedremo alcune strutture dati che permettono di eseguire in modo efficiente un determinato repertorio di operazioni: - B-alberi. - strutture dati per insiemi disgiunti.

B-alberi I B-alberi sono alberi bilanciati adatti per memorizzare grandi masse di dati in memoria secondaria. Sono simili agli alberi rosso-neri ma sono progettati per minimizzare gli accessi alla memoria secondaria. Le operazioni sui B-alberi sono Insert, Delete e Search alle quali si aggiungono Split e Join.

A differenza dei nodi degli alberi rosso-neri che hanno una sola chiave e due figli, i nodi dei B-alberi possono avere un numero di chiavi n ≥ 1 ed n+1 figli. Ecco un esempio di B-albero: M F G D H B CJ K LN PV WR SY Z Q T X T.root

Il funzionamento di una memoria esterna su disco può essere schematizzato nel seguente modo: Meccanismo di spostamento della testinaTestina di lettura-scrittura Disco Traccia Pagina Settore

L’accesso alle informazioni registrate su disco richiede alcune operazioni meccaniche: - spostamento della testina; - rotazione del disco. Le operazioni di lettura/scrittura hanno come unità elementare una pagina che normalmente contiene da 2 a 10 kbyte.

Il tempo di accesso al disco comprende il tempo per posizionare la testina di lettura scrittura e poi aspettare che la pagina passi sotto la testina. Esso è dell’ordine dei millisecondi. Tipicamente msec. Il tempo di accesso alla memoria RAM, non richiedendo operazioni meccaniche è invece dell’ordine dei nanosecondi. Tipicamente nsec.

Spesso occorre più tempo per leggere i dati da disco in memoria e per scrivere i risultati su disco di quanto serva per la loro elaborazione. 1.tempo di lettura-scrittura su disco (che assumiamo proporzionale al numero di pagine lette o scritte). 2.tempo di CPU (tempo di calcolo in memoria RAM). Per questo, nel valutare la complessità delle operazioni, terremo separate le due componenti:

L’operazione di lettura da disco è: dato il riferimento x ad un oggetto ricopia l’oggetto da disco in memoria. DiskRead(x) Assumiamo che sia possibile accedere al valore di un oggetto soltanto dopo che esso sia stato letto in memoria.

L’operazione di scrittura su disco è : che, dato il riferimento x ad un oggetto presente in memoria, copia tale oggetto su disco. DiskWrite(x) Tipicamente l’elaborazione di un oggetto residente su disco segue lo schema: DiskRead(x)..... // elaborazioni che accedono-modificano x DiskWrite(x) // non necessaria se x invariato..... // elaborazioni che non modificano x

Le operazioni di lettura-scrittura devono leggere e scrivere almeno una pagina e questo anche se l’oggetto x è molto più piccolo. Siccome, con i B-alberi, la maggior parte del tempo è utilizzata per le operazione di lettura-scrittura da disco è opportuno sfruttare al massimo ciascuna operazione.

Per questa ragione si usano nodi la cui dimensione sia circa quella di una pagina. Il numero massimo di figli di un nodo è limitato dalla dimensione di una pagina. Un numero massimo di figli compreso tra 50 e 2000 è la norma.

Un elevato grado di diramazione riduce di molto sia l’altezza dell’albero che il numero di letture da disco necessarie per cercare una chiave. 1 nodo 999 chiavi nodi chiavi nodi chiavi La figura seguente mostra che un B-albero di altezza 2 con un grado di diramazione 1000 può contenere chiavi (un miliardo) T.root

Poiché la radice può essere tenuta stabilmente in memoria, due accessi a disco sono sufficienti per cercare una qualsiasi chiave. Per semplicità di esposizione assumeremo che le informazioni associate a ciascuna chiave (o i puntatori a tali informazioni) siano memorizzate assieme alle chiavi in tutti i nodi dell’albero. Un’altra possibilità è mettere tali informazioni nelle foglie e mettere nei nodi interni soltanto chiavi e puntatori ai figli, massimizzando quindi il grado di diramazione dei nodi interni.

Esempio di B-albero con grado massimo 4 e che usa la prima tecnica: T.root M*D*H*Q*T*X*B*C*E*F*J*K*L*N*O*P*R*S*Y*Z*W* Ogni nodo deve poter contenere 3 chiavi, 4 puntatori ai figli e 3 puntatori alle informazioni associate 10 campi in totale

T.root MB*C*D*E*F*H*J*K*L*M*DHKN*O*P*Q*R*S*T*W*X*Y*Z*PSW Esempio di B-albero con grado massimo 4 e che usa la seconda tecnica Ogni nodo interno deve poter contenere 3 chiavi e 4 puntatori ai figli. Una foglia deve poter contenere 3 chiavi e 3 puntatori alle informazioni associate. al più 7 campi in totale

Definizione di B-albero Un B-albero T è un albero di radice T.root tale che: 1.Ogni nodo x contiene i seguenti campi: a) x.n : il numero di chiavi presenti nel nodo. b) x.key 1 ≤ x.key 2 ≤.... ≤ x.key x.n : le x.n chiavi in ordine non decrescente. c) x.leaf : valore booleano che è true se il nodo x è foglia, false altrimenti.

2. se il nodo non è una foglia contiene anche d)x.c 1, x.c 2,...., x.c x.n+1 : gli x.n+1 puntatori ai figli. 3.le chiavi x.key 1, x.key 2,...., x.key x.n di un nodo interno separano le chiavi dei sottoalberi. In altre parole, se k i è una chiave qualsiasi del sottoalbero di radice x.c i allora k 1 ≤ x.key 1 ≤ k 2 ≤... ≤ k x.n ≤ x.key x.n ≤ k x.n+1 4. Le foglie sono tutte alla stessa profondità h detta altezza dell’albero.

5.Vi sono limiti inferiori e superiori al numero di chiavi in un nodo e tali limiti dipendono da una costante t detta grado minimo del B-albero. a)ogni nodo, eccetto la radice, ha almeno t -1 chiavi e, se non è una foglia, ha almeno t figli. b)se l’albero non è vuoto, la radice contiene almeno una chiave. c)ogni nodo ha al più 2t -1 chiavi e, se non è foglia, ha al più 2t figli.

Ad ogni chiave sono generalmente associate delle informazioni ausiliarie. Assumeremo implicitamente che quando viene copiata una chiave vengano copiate anche tali informazioni. I B-alberi più semplici sono quelli con grado minimo t = 2. Ogni nodo interno ha 2,3 o 4 figli. In questo caso si dicono anche alberi. Storicamente i alberi sono predecessori sia dei B-alberi che degli alberi rosso-neri.

Esercizio 20. Perché non possiamo avere grado minimo t = 1? Esercizio 21. Calcolare il numero massimo di chiavi che può contenere un B-albero in funzione del suo grado minimo t e della sua altezza h Esercizio 22. Dire quale struttura dati si ottiene se in ogni nodo nero di un albero rosso-nero conglobiamo i suoi eventuali figli rossi.

Altezza di un B-albero Proprietà. Ogni B-albero non vuoto di grado minimo t con N chiavi ha altezza Dimostrazione. Invece della diseguaglianza dimostriamo quella equivalente

Sia T.root la radice ed h ≥ 0 l’altezza. Indichiamo con m i il numero di nodi a livello i. Allora m 0 = 1 m 1 = T.root.n + 1 ≥ 2 m i ≥ t m i -1 ≥ t i -1 m 1 per i > 1 e dunque m i ≥ 2t i -1 per ogni i ≥ 1

Quindi L’altezza di un B-albero è O(log t n) dello stesso ordine O(log 2 n) degli alberi rosso-neri. Ma la costante nascosta nella O è inferiore di un fattore log 2 t che, per 50 ≤ t ≤ 2000, è compreso tra 5 e 11.

Operazioni elementari Adottiamo le seguenti convenzioni per le operazioni sui B-alberi: 1. La radice del B-albero è sempre in memoria. 2. I nodi passati come parametri alle procedure sono stati preventivamente caricati in memoria.

Defineremo le seguenti operazioni: - Create costruttore di un B-albero vuoto - Search che cerca una chiave nell’albero - Insert che aggiunge una chiave - Delete che toglie una chiave Ci serviranno anche alcune procedure ausiliarie: - SplitChild - InsertNonfull - AugmentChild - DeleteNonmin - DeleteMax

Un B-albero vuoto si costruisce con la procedura: Create(T) x = AllocateNode() x.leaf = true x.n = 0 DiskWrite(x) T.root = x la cui complessità è O(1).

Search(x, k) i = 1, j = x.n+1 // x.key 1.. i-1 < k ≤ x.key j.. x.n while i < j // Ricerca binaria m =  (i+j)/2  if k ≤ x.key m j = m else i = m+1 // x.key 1.. i-1 < k ≤ x.key i.. x.n if i ≤ x.n and k == x.key i return (x, i) elseif x.leaf return nil else DiskRead(x.c i ) return Search(x.c i, k)

Il tempo di CPU di Search è: Il numero di DiskRead è al più uguale all’altezza h dell’albero e quindi O(log t N)

Non possiamo aggiungere la chiave ad un nodo interno perché dovremmo aggiungere anche un sottoalbero. L’aggiunta di una chiave ad un B-albero può avvenire soltanto in una foglia e soltanto se la foglia non è piena, cioè non ha il numero massimo 2t-1 di chiavi. Vediamo ora la Insert che aggiunge una chiave.

Possiamo garantirci che la foglia a cui arriveremo non sia piena se ad ogni passo nella discesa dalla radice ci assicuriamo che il figlio su cui scendiamo non sia pieno. Nel caso in cui tale figlio sia pieno chiamiamo prima una particolare funzione SplitChild che lo divide in due. La stessa cosa dobbiamo fare all’inizio se la radice è piena.

Ecco come funziona Insert se la radice è piena in un B-albero di grado minimo t = 4 root P Q R S T U V T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8 dopo di che richiama SplitChild root.c 1 y root P Q R S T U V T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8

Ecco come funziona SplitChild su un figlio pieno in un B-albero di grado minimo t = 4... N W... x x.c i y... N S W... x x.c i P Q R y T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8 T U V x.c i+1 z P Q R S T U V T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8

SplitChild(x, i, y) // x non pieno con y figlio i-esimo pieno z = Allocate-Node() z.leaf = y.leaf z.n = t-1 // Sposta le ultime t -1 chiavi di y in z for j = 1 to t -1 z.key j = y.key j+t // Se y non è foglia sposta in z gli ultimi t puntatori di y if not y.leaf for j = 1 to t z.c j = y.c j+t y.n = t-1

// Mette la t-esima chiave di y in x for j = x.n downto i x.key j +1 = x.key j x.key i = y.key t // Aggiunge z come i+1-esimo figlio di x for j = x.n+1 downto i+1 x.c j +1 = x.c j x.c i+1 = z x.n = x.n +1 // Scrive su disco i nodi modificati DiskWrite(x), DiskWrite(y), DiskWrite(z)

Insert(T, k) r = T.root if r.n == 2t –1 s = AllocateNode() T.root = s s.n = 0, s.c 1 = r, s.leaf = false SplitChild(s,1, s.c 1 ) r = s InsertNonfull(r, k) La procedura Insert in un B-albero è:

InsertNonfull(x,k) // x non è pieno if x.leaf // Aggiunge k al nodo x i = x.n while i ≥ 1 and x.key i > k x.key i+1 = x.key i, i = i -1 x.key i+1 = k, x.n = x.n+1 DiskWrite(x)

else // Cerca il figlio in cui inserirla i = 1, j = x.n+1 // x.key 1..i-1 < k ≤ x.key j..x.n while i < j // Ricerca binaria m =  (i+j)/2  if k ≤ x.key m j = m else i = m+1 DiskRead(x.c i ) if x.c i.n == 2t -1 SplitChild(x, i, x.c i ) if k > x.key i i = i +1 InsertNonfull(x.c i, k)

La procedura SplitChild richiede al più 3 DiskWrite e tempo di CPU O(1) (considerando t costante). Se x è radice di un sottoalbero di altezza h allora InsertNonfull(x, k) nel caso pessimo effettua una DiskRead e una SplitChild ad ogni livello per un totale di 4h accessi a disco e richiede tempo di CPU O(h). Quindi Insert richiede O(h) accessi a disco e O(h) tempo di CPU.

G M P X A C D EJ KN OR S T U VY Z Insert(T,B ) A B C D E G M P X J KN OR S T U VY Z

Insert ( T,Q) A B C D E G M P X J KN OR S T U VY ZJ KN OA B C D E G M P T X Y ZQ R SU V

Insert ( T,L) J KN OA B C D E G M P T X Y ZQ R SU V J K LN OA B C D E G M Y ZQ R SU V P T X

Insert ( T,F) J K LN OA B C D E G M Y ZQ R SU V P T X J K LN OA B C G M Y ZQ R SU V P T X D E F

Esercizio 23 Scrivere una funzione che cerca la chiave minima contenuta in un B-albero ed una funzione che data un valore c cerca la minima chiave k > c presente nel B-albero. Esercizio 24* In un B-albero con grado minimo t = 2 vengono inserite le chiavi 1,2,3,....,n nell’ordine. Valutare il numero di nodi dell’albero in funzione di n.

Esercizio 25 Supponiamo che la dimensione di un nodo di un B-albero si possa scegliere arbitrariamente e che il tempo medio per leggere un nodo da disco sia a+bt dove t è il grado minimo ed a e b sono due costanti Suggerire un valore ottimo per t quando a = 30ms e b = 40  s

Possiamo togliere una chiave solo da una foglia e possiamo farlo soltanto se la foglia non ha il minimo numero di chiavi t -1 (o se la foglia è anche la radice). Togliere una chiave da un B-albero è un po’ più complicato che inserirla.

Se dobbiamo togliere una chiave k = x.key i da un nodo x che non è foglia scambiamo prima la chiave k con la massima chiave k' del sottoalbero di radice x.c i. k' = y.key y.n è l’ultima chiave dell’ultima foglia y del sottoalbero di radice x.c i. Inoltre nel B-albero non vi è alcuna chiave di valore compreso tra k' e k.

Scendendo dalla radice per cercare la chiave k da togliere ci dobbiamo anche assicurare che i nodi su cui ci spostiamo non abbiano mai il numero di chiavi minimo t-1. Se il nodo figlio su cui dobbiamo scendere ha solo t-1 chiavi, prima di scendere dobbiamo aumentarlo. Possiamo farlo prendendo una chiave da uno dei fratelli vicini o, se questo non è possibile, riunendolo con uno dei fratelli vicini.

Delete(T,F ) J K LN OA B C G M Y ZQ R SU V P T X D E FJ K LN OA B C G M Y ZQ R SU V P T X D E

Delete(T,M ) J K LN OA B C G M Y ZQ R SU V P T X D EJ K MN OA B C G L Y ZQ R SU V P T X D E

J K MN OA B C G L Y ZQ R SU V P T X D EJ KN OA B C G L Y ZQ R SU V P T X D E

Delete(T,G ) J KN OA B C G L Y ZQ R SU V P T X D EN OA B C L Y ZQ R SU V P T X D E G J K

N OA B C L Y ZQ R SU V P T X D E J KN OA B C L Y ZQ R SU V P T X D E G J K

Delete(T,D ) N OA B C L Y ZQ R SU V P T X D E J KN OA B C L P T X Y ZQ R SU V D E J K

N OA B C L P T X Y ZQ R SU V D E J K N OA B C L P T X Y ZQ R SU VE J K

N OA B C L P T X Y ZQ R SU VE J K N OA B C L P T X Y ZQ R SU VE J K

N OA B C L P T X Y ZQ R SU VE J K Delete(T,B ) N OA B C E L P T X Y ZQ R SU VJ K

N OA C E L P T X Y ZQ R SU VJ KN OA B C E L P T X Y ZQ R SU VJ K

Delete(T, k) if T.root.n ≠ 0 DeleteNonmin(T.root, k) if T.root.n == 0 and not T.root.leaf T.root = T.root.c 1 La procedura di rimozione di una chiave da un B- albero è: si limita a richiamare la funzione ausiliaria DeleteNonmin sulla radice dell’albero dopo essersi assicurata che l’albero non sia vuoto. Se al ritorno la radice è vuota e non è una foglia essa viene sostituita con il figlio.

La procedura DeleteNonmin usa la procedura AugmentChild per assicurarsi di scendere sempre su di un nodo che non contiene il minimo numero di chiavi. La procedura AugmentChild aumenta il numero di chiavi del figlio i-esimo prendendo una chiave da un fratello vicino. Se entrambi i fratelli vicini hanno anch’essi il minimo numero di chiavi allora riunisce il figlio i-esimo con uno dei fratelli.

AugmentChild(x, i, z) // x ha almeno t chiavi // z, figlio i-esimo di x, ha solo t-1 chiavi if i > 1 // z ha un fratello alla sua sinistra y = x.c i-1, DiskRead(y) if i > 1 and y.n > t-1 // si può togliere una chiave // dal fratello sinistro.... N R... x K L M T 1 T 2 T 3 T 4 T 5 T 6 T 7 P Q x.c i-1 x.c i yz.... M R... K L T 1 T 2 T 3 T 4 T 5 T 6 T 7 N P Q x.c i-1 x.c i x yz

// Sposta avanti le chiavi in z for j = z.n+1 downto 2 z.key j = z.key j-1 // Rotazione delle chiavi z.key 1 = x.key i-1 x.key i-1 = y.key y.n if not z.leaf // Sposta anche i puntatori in z for j = z.n+2 downto 2 z.c j = z.c j-1 z.c 1 = y.c y.n+1 z.n = z.n+1, y.n = y.n-1 DiskWrite(x), DiskWrite(y), DiskWrite(z)

else // i = 1 o il fratello sinistro y ha solo t -1 chiavi if i ≤ x.n // z ha un fratello alla sua destra w = x.c i+1, DiskRead(w) if i ≤ x.n and w.n > t -1 // si può togliere una chiave al fratello destro... L R... P Q T 1 T 2 T 2 T 4 T 5 T 6 T 7 S T U x x.c i+1 x.c i z w... L S... P Q R T 1 T 2 T 3 T 4 T 5 T 6 T 7 T U x x.c i+1 x.c i zw

// Rotazione delle chiavi z.key z.n+1 = x.key i x.key i = w.key 1 // Sposta indietro le chiavi in w for j = 2 to w.n w.key j -1 = w.key j if not w.leaf // Sposta anche i puntatori z.c z.n+2 = w.c 1 for j = 2 to w.n+1 w.c j -1 = w.c j z.n = z.n+1, w.n = w.n-1 DiskWrite(x), DiskWrite(y), DiskWrite(z)

else // i = 1 oppure i = x.n+1 ma non entrambi // Se i < x.n+1 esiste w che ha solo t -1 chiavi // Se i > 1 esiste y che ha solo t -1 chiavi if i ≤ x.n // z ha il fratello destro w con t -1 chiavi // Possiamo riunire z, x.key i e w... M R U... P Q T 1 T 2 T 3 T 4 T 5 T 6 S T x z w x.c i+1 x.c i P Q R S T T 1 T 2 T 3 T 4 T 5 T 6... M U... x z x.c i

z.key t = x.key i // Aggiunge x.key i a z for j = i +1 to x.n // Sposta chiavi e puntatori in x x.key j -1 = x.key j for j = i+2 to x.n+1 x.c j -1 = x.c j x.n = x.n-1 for j = 1 to w.n // Copia le chiavi di w in z z.key j +t = w.key j if not w.leaf for j = 1 to w.n+1 // Copia i puntatori di w in z z.c t+ j = w.c j z.n = 2t -1 DiskWrite(x), DiskWrite(z)

else // i = x.n+1 // e z ha fratello sinistro y con t-1 chiavi // Possiamo riunire y, x.key x.n e z J M K L T 1 T 2 T 3 T 4 T 5 T 6 N P x zy x.c x.n x.c x.n+1 K L M N P T 1 T 2 T 3 T 4 T 5 T J x y x.c x.n

// Sposta x.key x.n in y y.key t = x.key x.n x.n = x.n-1 // Copia le chiavi di z in y for j = 1 to z.n y.key j +t = z.key j if not y.leaf // Copia anche i puntatori di z in y for j = 1 to z.n+1 y.c t+ j = z.c j y.n = 2t -1 DiskWrite(x), DiskWrite(y)

DeleteNonmin(x, k) // x ha almeno t chiavi i = 1, j = x.n+1 // x.key 1..i-1 < k ≤ x.key j..x.n m =  (i+j)/2  while i < j if k ≤ x.key m j = m else i = m+1 if i ≤ x.n and k == x.key i // Trovata k in x if x.leaf // x è una foglia, posso togliere k // spostando indietro le chiavi successive for j = i+1 to x.n x.key j-1 = x.key j x.n = x.n-1 DiskWrite(x)

else // Trovata k in x ma x non è una foglia DiskRead(x.c i ) if x.c i.n == t -1 AugmentChild(x, i, x.c i ) if i == x.n+2 // riuniti gli ultimi due figli i = i - 1 if x.key i ≠ k // k spostata in x.c i DeleteNonmin(x.c i, k) else // è rimasto k = x.key i. Sposto in x.key i la // massima chiave del sottoalbero x.c i e poi // elimino tale chiave dal sottoalbero DeleteMax(x, i, x.c i )

else // k non è in x // Può stare solo nel sottoalbero i-esimo DiskRead(x.c i ) if x.c i.n == t -1 AugmentChild(x, i, x.c i ) if i == x.n+2 // riuniti gli ultimi due figli i = i - 1 DeleteNonmin(x.c i, k)

DeleteMax(x, i, y) // y ha almeno t chiavi // Sposta in x.key i la massima chiave // del sottoalbero di radice y if y.leaf // sposto in x.key i l’ultima chiave di y x.key i = y.key y.n y.n = y.n-1 DiskWrite(x), DiskWrite(y) else // scendo sull’ultimo figlio z = y.c y.n+1 DiskRead(z) if z.n == t -1 AugmentChild(y, y.n+1, z) // y.n è aggiornato DeleteMax(x, i, y.c y.n+1 )

La procedura AugmentChild richiede al più 5 accessi a disco (due DiskRead e tre DiskWrite) e tempo di CPU O(1) (considerando t costante). Se y è radice di un sottoalbero di altezza h allora DeleteMax(x, i, y) nel caso pessimo effettua 5h accessi a disco e richiede tempo di CPU O(h). Se x è radice di un sottoalbero di altezza h allora DeleteNonmin(x, k) nel caso pessimo effettua 6h accessi a disco e richiede tempo di CPU O(h).

Esercizio 26. Si vuole aumentare un B-albero aggiungendo ad ogni nodo x un campo h che contiene l’altezza del sottoalbero di radice x. Dire quali sono le modifiche da apportare a Insert e Delete. Assicurarsi che la complessità asintotica non aumenti.

Esercizio 27. Siano dati due B-alberi T' e T'' ed una chiave k tale che T' < k < T''. Scrivere un algoritmo che riunisce T', k e T'' in un unico B-albero T (operazione join). Se T' e T'' hanno altezze h' ed h'' l’algoritmo deve avere complessità O(|h'-h''|+1).

Se h' = h'' : kk n' + n'' + 1 ≤ 2t -1 kk n' + n'' + 1 > 2t -1 ed n',n'' ≥ t -1

mkm k n' + n'' + 1 > 2t -1 ed n'' < t -1

Se h' > h'' : k kk k

k m mk

Esercizio 28* Siano dati un B-albero T ed una chiave k di T. Trovare un algoritmo che divide T in un B- albero T' contenente le chiavi minori di k, la chiave k e un B-albero T'' contenente le chiavi maggiori di k (operazione split). L’algoritmo deve avere complessità O(h+1) in cui h è l’altezza di T.

k