B-Alberi Esercitazioni
Introduzione B-Trees: alberi bilanciati di ricerca progettati per essere memorizzati su dischi magnetici. Dischi magnetici molto più lenti delle memorie ad accesso casuale. La misura dell efficienza di un B-albero è data anche dal numero di accessi che si effettuano al disco. Il numero di accessi al disco aumenta con l aumentare dell altezza dell albero. Mantenere bassa l altezza del B-albero.
DISK-READ(x) DISK-WRITE(x) elle applicazioni dei B-alberi viene elaborata una grande uantità di dati al punto che spesso la memoria principale on è sufficiente. oluzione: iene copiato un nodo alla volta dal disco alla memoria rincipale: procedura DISK-READ(x). opo la sua elaborazione, viene riscritto nel disco se è stato odificato: procedura DISK-WRITE(x).
Esempio di B-albero root[t] M D H Q T X B C F G J K L N P R S V W Y Z Se un nodo x di un B-albero contiene n[x] chiavi, allora x ha n[x]+1 figli.
Definizione dei B-alberi (I). Ogni nodo x è caratterizzato dai seguenti campi o attributi: a) n[x], numero delle chiavi memorizzate in x; b) le n[x] chiavi sono ordinate in modo non decrescente: key 1 [x]<= key 2 [x]<= <= key n[x] [x]; c) leaf[x], un valore boolean cheè TRUE se x è una foglia, FALSE altrimenti.. Un nodo interno x contiene n[x]+1 puntatori, c 1 [x], c 2 [x],, c n[x]+1 [x ai suoi figli.. I campi key i [x] definiscono gli intervalli delle chiavi memorizzate in ogni sottoalbero: se k i è una qualunque chiave memorizzata nel sottoalbero, di radice c i [x], allora k 1 <=key 1 [x]<=k 2 <=key 2 [x]<= <=key n[x] [x]<=k n[x]+1
Definizione dei B-alberi (II) 4. Tutte le foglie sono alla stessa profondità, che coincide con l altezza dell albero. 5. Il numero di chiavi che un nodo può contenere è limitato (superiormente e inferiormente); dipende dal grado minimo t>=2: a) Ogni nodo (ad eccezione della radice) deve contenere almeno t-1 chiavi. Ogni nodo interno (ad eccezione della radice) deve avere almeno t figli. Se non è un albero vuoto, la radice deve contenere almeno una chiave. b) Ogni nodo può contenere al massimo 2t-1 chiavi, quindi un nodo interno può avere al massimo 2t figli.
Definizione dei B-alberi (III) Riassumendo: Grado minimo Limite numero figli Numero di figli di x Numero di chiavi di x Numero di chiavi di root t >= 2 t <= c[x] <= 2t c[x] = n[x]+1 t-1 <= n[x] <= 2t-1 1 <= n[root] <= 2t-1
Esercizio 1 Per quale valore di t, il seguente è un B-albero lecito? M D H Q T X B C F G J K L N P R S V W Y Z
Esercizio 2 (I) Si mostrino alcuni B-alberi leciti con grado minimo uguale a 2 che rappresentano l insieme di chiavi {1, 2, 3, 4, 5} t=2 key={1, 2, 3, 4, 5} t-1 <= n[x] <= 2t-1 1<= n[x] <= 3 t <= c[x] <= 2t 2 <= c[x] <= 4
Esercizio 2 (II) c[x] = 2 n[x] = 1 2 3 4 1 3 4 5 1 2 4 5 1 2 3 5 c[x] = 3 n[x] = 2 2 4 1 3 5
Esercizio 3 (I) Si derivi un limite superiore stretto del numero delle chiavi che possono essere memorizzate in un B-albero con altezza h in funzione del grado minimo t. h Σ [(2t) 2 * (2t-1)] i=0
Operazioni di base sui B-alberi Si assume che: a) la radice dell albero è sempre contenuta in memoria principale; b) tutti i nodi che vengono passati come parametro alle procedure sono già presenti in memoria principale. RICERCA DI UNA CHIAVE B-Tree-Search(x, k){ int i = 1; while(i<=n && k>key i [x]) i++; if(i<=n[x] && k=key i [x]) return (x, i) if(leaf(x)) return null; else {DISK-READ(c i [x]); return B-Tree-Search(c i [x], k) }
RICERCA DI UNA CHIAVE: complessità Per ricercare una chiave all interno di un B-albero, lo si percorre lungo un cammino che porta dalla radice alle foglie. Il numero di accessi al disco della procedura B-Tree- Search è dato da Θ(h)= Θ(log i n), dove h è l altezza e n è il numero di chiavi del B-albero. Siccome n[x]<2t, il tempo impiegato dal ciclo while per esaminare un nodo qualsiasi è O(t), pertanto il tempo totale di CPU è O(th)=O(t*log i n)
REAZIONE di un B-albero VUOTO B-Tree-Create(){ x=allocatenode(); leaf(x)=true; n[x]=0; DISK-WRITE(x); root[t]=x;} Dove AllocateNode() crea un nuovo nodo su disco.
DIVISIONE di un nodo in un B-albero (I) Procedura necessaria quando si deve inserire una nuova chiave in un nodo: se tale nodo è pieno*, non è possibile effettuare tale operazione quindi bisogna dividerlo. Il nodo viene diviso all altezza della sua chiave mediana key t [x]. Il risultato di questa operazione è che il nodo x viene diviso in due nodi con t-1 chiavi ciascuno. un nodo x è pieno quando n[x]= 2t-1
DIVISIONE di un nodo in un B-albero (II) -Tree-Split-Child(x, i, y){ z=allocatenode(); leaf(z)=leaf(y); n[z]=t-1; for(j=1; j<=t-1; j++;) key j [z]=key j+t [y]; if(!leaf(y)) for(j=1; j<=t; j++;) c j [z]=c j+t [y] n[y]=t-1; for(j=n[x]+1; j>=i+1; j--;) c j+1 [x]=c j [x]; c i+1 [x]=z; for(j=n[x]; j>=i; j--;) key j+1 [x]=key j [x]; key i [x]=key t [y]; n[x]++; } DISK-WRITE(y); DISK-WRITE(z); DISK-WRITE(x); Complessità: Il tempo di CPU richiesto dalla procedura B- Tree-Split-Child è di Θ(t), per la presenza dei due cicli for.
IVISIONE di un nodo in un B-albero (III) x key i-1 [x] key i [x] x key i-1 [x] key i [x] key i+1 [x] N W. N S W y=c i [x] P Q R S T U V y=c i [x] P Q R z=c i+1 [x] T U V T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8 T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8
INSERIMENTO di una chiave in un B-albero (I) Operazione che viene fatta attraverso una singola visita all albero di altezza h, che richiede O(h) accessi al disco. Il tempo di CPU richiesto è O(th)=O(t*log t n). La procedura B-Tree-Insert utilizza la la procedura B- Tree-Split-Child per garantire che non si inserisca mai la chiave in un nodo già pieno.
} INSERIMENTO di una chiave in un B-albero (II) B-Tree-Insert(T, k){ r =root[t]; if(n[r]==2t-1) { s=allocatenode(); root[t]=s; leaf(s)=false; n[s]=0; c 1 [s]=r; B-Tree-Split-Child(s,1,r); B-Tree-NonFull(s, k); } else B-Tree-Insert-NonFull(r, k);
INSERIMENTO di una chiave in un B-albero (III L operazione che divide la radice è l unica che fa aumentare di una unità l altezza dell albero. root[t s H root[t] r r A D F H L N P A D F L N P T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8 T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8
B-Tree-Insert-NonFull B-Tree-Insert-NonFull(x, k){ i=n[x]; if(leaf(x)){ while(i>=1 && k<key i [x]) { key i+1 [x]=key i [x]; i--; } key i+1 [x]=k; n[x]++; DISK-WRITE(x);} else{ while(i>=1 && k<key i [x]) i--; i++; DISK-READ(c i [x]); if(n[c i [x]]=2t-1){ B-Tree-Split-Child(x, i, c i [x]); if(k>key i [x]) i++; } B-Tree-Insert-NonFull(c i [x], k);}
ESERCIZIO 4 (I) Nel B-albero sottostante, inserire le seguenti chiavi: {B, Q, L, F}. root[t] G M P X A C D E J K N O R S T U V Y Z
ESERCIZIO 4 (II) root[t] G M P X A B C D E J K N O R S T U V Y Z root[t] G M P T X A B C D E J K N O Q R S U V Y Z
ESERCIZIO 4 (III) root[t] P G M T X A B C D E J K L N O Q R S U V Y Z root[t] P C G M T X A B D E F J K L N O Q R S U V Y Z
ESERCIZIO 5 (I) Si mostrino i risultati dell inserzione della sequenza di chiavi {F, S, Q, K, C, L, H, T} per t=2.