Universita' di Ferrara
Facolta' di Scienze Matematiche, Fisiche e Naturali
Laurea Specialistica in Informatica
Algoritmi Avanzati
2-3 BTree
Copyright © 2006-2009 by Claudio Salati.
Lez. 14
1
Rappresentazione di alberi
• Negli esercizi si e' visto come, aumentando le informazioni
presenti in un un nodo di albero binario,
aggiungendo una informazione di dimensione del sottoalbero
sia possibile inserire nuovi elementi in un albero binario
mantenendolo bilanciato.
N.B.: la procedura di inserimento bilanciato vista negli
esercizi non consente pero' di mantenere ordinato l'albero!
• Negli esercizi si e' anche visto come una infomazione di nodo
arricchita
l'informazione sull'identita' del proprio genitore
consenta anche una navigazione piu' efficiente nell'albero.
consente ad esempio di localizzare facilmente nell'insieme
l'elemento immediatamente successivo ad un elemento dato.
2
Cosa si vorrebbe
• quello che si vorrebbe e' potere effettuare inserimenti, rimozioni,
ricerche in un albero ordinato (in termini di valori o in termini di
posizione degli elementi) mantenendolo anche bilanciato
in realta': mantenendo l'altezza dell'albero logaritmica nel
numero di nodi in esso contenuti.
• notare che un albero potrebbe anche essere visto come una
struttura di indiciamento di un vettore di dimensione dinamica:
consentendoci in tempo logaritmico di:
• leggere il k-esimo elemento del vettore, se questo elemento
esiste.
• scrivere il k-esimo elemento del vettore, se questo
elemento esiste.
• inserire un nuovo elemento in una qualunque posizione del
vettore (in particolare in fondo al vettore), estendendo cosi’
il vettore.
• eliminare un elemento in una qualunque posizione del
vettore, riducendone cosi' la dimensione.
3
in realta' e' proprio quello che succede nel file system Unix!
La risposta: BTree
• Un 2-3 BTree e' un albero ordinato con le seguenti proprieta':
• i dati (l'informazione inserita nell'albero) sono contenuti solo
nelle foglie dell'albero (questa e' in realta' una semplificazione).
• tutte le foglie dell'albero si trovano alla stessa profondita'.
• ogni nodo non terminale (che non e' una foglia) ha 2 o 3 figli.
• i nodi non terminali dell'albero mantengono la strutturazione
della base dati.
• Per mantenere la strutturazione della base dati i nodi non terminali
possono/devono contenere informazioni strutturali addizionali.
• Le informazioni mantenute dai nodi non terminali dipendono
dall'ADT che si vuole realizzare tramite il BTree, e.g.:
• un vettore di dimensioni variabili
• una tabella associativa (una mappa chiaveinformazione)
• l'efficienza del BTree e' legata all'informazione strutturale
mantenuta nei nodi non terminali.
4
Informazione strutturale
• Informazione strutturale che puo' essere mantenuta sui nodi non
terminali
• tipo del nodo: non-terminale (per una foglia sarebbe: terminale).
• altezza del nodo (N.B.: sostituisce l’informazione precedente, in
quanto e’ =0 per una foglia, 0 per un nodo non terminale).
• numero di foglie (elementi di informazione) sotteso dal nodo
per un ADT vettore.
• elemento di informazione minimo e/o massimo contenuto nel
nodo
per un ADT tabella asoociativa.
• N.B.: ogni nodo mantiene (deve mantenere) informazioni solo sul
proprio sotto-albero.
• E' ovvio che l'informazione mantenuta in un nodo deve essere
aggionata congruentemente quando la struttura del suo sottoalbero viene modificata.
perche’ aggiungo o cancello una foglia
5
Informazione strutturale
• In quanto appartenenti ad un albero tutti i nodi, terminali o meno,
devono mantenere le relazioni caratteristiche dell'albero stesso:
• relazione padre figli
• 2 o 3 in caso di nodo non terminale, e ovviamente occorre
anche sapere quanti figli ha il nodo
• 0 in caso di foglia
• relazione figlio padre
6
BTree: una possibile definizione
struct header {
elemento
int
struct node
max;
size;
*child; };
struct inode {
int
struct header
kidsNo;
children[3]; };
struct node {
int
tipo;
#define ELEMENTO 0
#define NODO
1
union {elemento
val;
struct inode inodo;
}
choice; };
#define STRUCT_NODE (sizeof(struct node))
typedef struct node *bTree;
7
Esempio
• 2-3 BTree in cui viene mantenuta l'informazione sul numero di
foglie (elementi di informazione) sotteso dal nodo.
n0 - 12
n1 - 7
n3 - 2
n4 - 2
e1
e3
e2
n2 - 5
n5 - 3
e4
e5
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
8
Esempio: come si trova il k-esimo elemento?
1
• Supponiamo di volere trovare il valore del k-esimo elemento
informativocontenuto nel BTree, con k=9.
L’elemento v[9] del vettore v concretamente rappresentato dal
BTree
• parto dal nodo radice n0, e voglio l'elemento di indice 9 nel relativo
sotto-albero
• 9 12: l'elemento esiste
• 9 > 7: l'elemento non e' nel primo sotto-albero di n0
• 9 (7+5): l'elemento e' nel secondo sotto-albero di n0, cioe' n2,
ed e' l'elemento (9-7), cioe' 2, di questo sottoalbero
• Continua alla pagina seguente
9
Esempio: come si trova il k-esimo elemento?
2
• Continua dalla pagina precedente
• vado dal nodo n2, e voglio l'elemento di indice 2 nel relativo sottoalbero
• 2 5: l'elemento esiste
• 2 2 : l'elemento e' nel primo sotto-albero di n2, cioe' n6, ed e'
l'elemento (2-0), cioe' 2, di questo sottoalbero
• vado dal nodo n6, e voglio l'elemento di indice 2 nel relativo sottoalbero
• 2 2: l'elemento esiste
• i figli sono foglie: l'elemento e' la secoda foglia di questo
sottoalbero, cioe' l'elemento e9
10
Complessita' delle operazioni su un 2-3 BTree
• Durante l'operazione abbiamo visitato solo i nodi che vanno dalla
radice alla foglia desiderata.
• cioe' un numero di nodi pari all'altezza dell'albero.
• l'altezza di un 2-3 BTree e' del logaritmo in base 2 del numero delle
sue foglie, in quanto ogni nodo non terminale ha almeno 2 foglie.
• pertanto, se le operazioni sul BTree coinvolgono solo nodi sul
percorso dalla radice alla foglia selezionata, queste operazioni hanno
complessita O(log(n)) rispetto al numero n di elementi informativi
contenuti nell'albero.
• notare che il numero complessivo di nodi dell'albero puo' arrivare fino
a 2*n-1, essendo che i nodi non terminali sono al piu' n-1.
• Esercizio:
• Quanti nodi ci sono un un 2-3 Btree di altezza h?
• Dualmente, quanto e’ alto un 2-3 Btree che ha n foglie?
11
Ricerca di un elemento (key-based)
struct node *search_2_3(bTree t, elemento e) {
if (t == NULL)
return (NULL);
else if (t->tipo == ELEMENTO)
if (t->choice.val == e) return (t);
else return (NULL);
else { // t->tipo == NODO
for (int k = 0; k < t->choice.inodo.kidsNo; k += 1) {
if (t->choice.inodo.children[k].max >= e)
return
(search_2_3(t->choice.inodo.children[k].child,
e));
// end if
}
return (NULL);
}
}
12
Esempio 1: inserimento di un nuovo elemento
.1
• Supponiamo di volere inserire un nuovo elemento informativo eN
in posizione 4.
n0 - 12
n1 - 7
n3 - 2
e1
e2
n2 - 5
n4 - 2
e3
n5 - 3
e4
e5
n6 - 2
e6
e7
e8
n7 - 3
e9
e10
e11
e12
eN
13
Inserimento di un nuovo elemento
1.2
• Poiche' il nodo n4 ha solo due figli possiamo aggiungergliene
ancora 1.
• Ovviamente occorre aggiornare le informazioni strutturali
contenute in
• n4, che ora contiene 3 foglie,
• n1, che ora contiene 8 foglie, ed
• n0, che ora contiene 13 foglie.
• Notare che si sono dovute aggiornare le informazioni strutturali
solo nei nodi che appartengono al path tra la foglia inserita e la
radice.
• Per cui la complessita' dell'operazione e' O(log(n))
14
Inserimento di un nuovo elemento
1.3
• Inserimento di un nuovo elemento informativo eN in posizione 4.
n0 - 13
n1 - 8
n3 - 2
e1
e2
n2 - 5
n4 - 3
e3
n5 - 3
e4
e5
n6 - 2
e6
e7
e8
n7 - 3
e9
e10
e11
e12
eN
15
Esempio 2: inserimento di un nuovo elemento
1
• Supponiamo di volere inserire un nuovo elemento informativo eN
in posizione 11.
n0 - 12
n1 - 7
n3 - 2
e1
e2
n2 - 5
n4 - 2
e3
e4
n5 - 3
e5
e6
n6 - 2
e7
e8
e9
n7 - 3
e10
e11
e12
eN
16
Inserimento di un nuovo elemento
2.2
• Poiche' il nodo n7 ha gia' 3 figli non possiamo aggiungergliene
ancora 1.
• Quindi il nodo n7 deve essere suddiviso in 2 nodi, n7' e n7",
ciascuno con due figli (e due foglie sottese).
• A questo punto il nodo n2 si trova ad avere 3 figli, il che e'
legittimo.
• Ovviamente occorre correggere le informazioni strutturali di
• n2, che ora contiene 6 foglie, ed
• n0, che ora contiene 13 foglie.
• Notare che
• si sono dovute effettuare suddivisioni, o
• si sono dovute aggiornare le informazioni strutturali
solo nei nodi che appartengono al path tra la foglia inserita e la
radice.
• Per cui la complessita' dell'operazione e' O(log(n))
17
Inserimento di un nuovo elemento
2.3
• Inserimento di un nuovo elemento informativo eN in posizione 11.
n0 - 13
n1 - 7
n3 - 2
e1
e2
n2 - 6
n4 - 2
e3
e4
n5 - 3
e5
e6
n6 - 2
e7
e8
n7' - 2
e9
n7" - 2
e10
e11
e12
eN
18
Esempio 3: inserimento di un nuovo elemento
1
• Supponiamo di volere inserire un nuovo elemento informativo eN
in posizione 7.
n0 - 12
n1 - 7
n3 - 2
e1
e2
n2 - 5
n4 - 2
e3
e4
n5 - 3
e5
n6 - 2
e6
e7
e8
n7 - 3
e9
e10
e11
e12
eN
19
Inserimento di un nuovo elemento
3.2
• Poiche' il nodo n5 ha gia' 3 figli non possiamo aggiungergliene
ancora 1.
• Quindi il nodo n5 deve essere suddiviso in 2 nodi, n5' e n5",
ciascuno con due figli (e due foglie sottese).
• A questo punto pero' il nodo n1 si troverebbe ad avere 4 figli, il che
e' illegittimo, per cui anch'esso andra' suddiviso in due nodi n1' e
n1", entrambi con 2 figli.
• Adesso n0 ha 3 figli, n1', n1" e n2: ma questo e' perfettamente
legittimo.
• Ovviamente occorre correggere le informazioni strutturali di
• n0, che ora contiene 13 foglie.
• Ed occorre anche compilare correttamente le informazioni
strutturali di n1' e n1", indicando il numero corretto di foglie da essi
sottese.
• Sia n1' che n1" contengono quattro foglie
20
Inserimento di un nuovo elemento
3.3
• Notare che, anche in questo caso,
• si sono dovute effettuare suddivisioni, o
• si sono dovute aggiornare le informazioni strutturali
solo nei nodi che appartengono al path tra la foglia inserita e la
radice.
• Per cui la complessita' dell'operazione e' O(log(n))
21
Inserimento di un nuovo elemento
3.4
• Inserimento di un nuovo elemento informativo eN in posizione 7.
n0 - 12
n1' - 4
n3 - 2
e1
e2
n1" - 4
n4 - 2
e3
n5' - 2
e4
e5
n2 - 5
n5" - 2
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
eN
22
Esempio 4: inserimento di un nuovo elemento
1
• Supponiamo di volere inserire un nuovo elemento informativo eN
in posizione 7 nell'albero costituito dai nodi non terminali n0, …,
n3, e dalle foglie e1, …, e7.
n0 - 7
n1 - 2
e1
e2
n2 - 2
e3
e4
n3 - 3
e5
e6
eN
e7
23
Inserimento di un nuovo elemento
4.2
• Poiche' il nodo n3 ha gia' 3 figli non possiamo aggiungergliene
ancora 1.
• Quindi il nodo n3 deve essere suddiviso in 2 nodi, n3' e n3",
ciascuno con due figli (e due foglie sottese).
• A questo punto pero' il nodo n0 si troverebbe ad avere 4 figli, il che
e' illegittimo, per cui anch'esso andra' suddiviso in due nodi n0' e
n0", entrambi con 2 figli.
• Avendo suddiviso la radice dell'albero in due, e' necessario creare
una nuova radice r che abbia n0' e n0" come sotto-alberi
in questo modo si e' dovuto aumentare di uno l'altezza
dell'albero.
• Ed occorre anche compilare correttamente le informazioni
strutturali di n0', n0" e r indicando il numero corretto di foglie da
essi sottese.
•
sia n0' che n0" contengono quattro foglie.
•
r sottende 8 foglie
24
Inserimento di un nuovo elemento
4.3
• Si ottiene quindi l'albero indicato di sotto.
r-8
n0 - 4
n1 - 2
e1
e2
n0 - 4
n2 - 2
e3
e4
n3' - 2
e5
e6
n3" - 2
eN
e7
25
Inserzione di un elemento (key-based)
struct result {
int
#define NONE
#define TOLEFT
#define TORIGHT
struct header
struct header
newEl;
0
1
2
old;
new; };
26
Inserzione di un elemento (key-based)
struct node *getLeaf(elemento e) {
struct node *temp = malloc(STRUCT_NODE);
temp->tipo = ELEMENTO;
temp->choice.val = e;
return (temp);
}
struct header makeLeafHeader(struct node *n) {
struct header head;
head.size = 1;
head.child = n;
head.max = n->choice.val;
return (head);
}
27
Inserzione di un elemento (key-based)
struct node *getNewNode(struct header h0,
struct header h1) {
struct node *temp = malloc(STRUCT_NODE);
temp->tipo = NODO;
temp->choice.inodo.kidsNo = 2;
temp->choice.inodo.children[0] = h0;
temp->choice.inodo.children[1] = h1;
return (temp);
}
struct header getNodeAndMakeHeader(struct header h1,
struct header h2) {
struct header temp;
temp.size = h1.size + h2.size;
temp.max = h2.max;
temp.child = getNewNode(h1.child, h2.child);
return (temp);
}
28
Inserzione di un elemento (key-based)
void setNONEResult(struct node *n, struct result *res) {
res->newEl = NONE;
res->old.child = n;
if (n->choice.inodo.kidsNo == 2) {
res->old.max = n->choice.inodo.children[1].max;
res->old.size = n->choice.inodo.children[0].size +
n->choice.inodo.children[1].size;
} else { // .kidsNo == 3
res->old.max = n->choice.inodo.children[2].max;
res->old.size = n->choice.inodo.children[0].size +
n->choice.inodo.children[1].size +
n->choice.inodo.children[2].size;
}
}
29
Inserzione di un elemento (key-based)
void setHeader2(struct node *n, struct header *h)
{
h->max = n->choice.inodo.children[1].max
h->size = n->choice.inodo.children[0].size +
n->choice.inodo.children[1].size;
h->child = n;
}
30
Inserzione di un elemento (key-based)
int getPos(struct node*n, elemento e) {
int pos;
for(pos = 0; pos <= n->choice.inodo.kidsNo; pos += 1)
if (e < n->choice.inodo.children[pos].max)
break;
// end if
// end for
return (pos);
}
31
Inserzione di un elemento (key-based)
void insert_2_3(bTree *t, elemento e) {
if (*t == NULL) {
*t = getLeaf(e);
} else if ((*t)->tipo == ELEMENTO) {
struct header oldH = makeLeafHeader(*t);
struct header newH = makeLeafHeader(getLeaf(e));
if (e > (*t)->choice.val) {
*t = getNewNode(oldH, newH);
} else {
*t = getNewNode(newH, oldH);
}
} else {
// tipo == NODO
// continua alla prossima pagina
32
Inserzione di un elemento (key-based)
// continua void insert_2_3(bTree *t, elemento e)
// tipo == NODO
struct result res = nodeInsert(*t, e);
switch (res.newEl) {
case NONE :
break;
case TOLEFT : { *t = getNewNode(res.new,
res.old);
break;
}
case TORIGHT : { *t = getNewNode(res.old,
res.new);
break;
}
}
}
}
33
Inserzione di un elemento (key-based)
struct result nodeInsert(struct node *n, elemento e) {
struct result res;
if (n->choice.inodo.children[0].size == 1) {
// i figli sono foglie
int pos = getPos(n, e);
if (n->choice.inodo.kidsNo == 2) {
// i figli sono foglie e sono 2
for (int shift = 3; shift > pos; shift -= 1)
n->choice.inodo.children[shift] =
n->choice.inodo.children[shift-1];
// end for
n->choice.inodo.children[pos] =
makeLeafHeader(getLeaf(e));
n->choice.inodo.kidsNo = 3;
setNONEResult(n, &res);
} else {
// i figli sono foglie e sono 3
34
// continua nella pagina seguente
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// i figli sono foglie e sono 3
struct header newHead = makeLeafHeader(getLeaf(e));
int pos = getPos(n, e);
if (pos <= 1) {
res.newEl = TOLEFT;
if (pos == 0) {
res.new = getNodeAndMakeHeader
(newHead,
n->choice.inodo.children[0]);
} else {
res.new = getNodeAndMakeHeader
(n->choice.inodo.children[0],
newHead);
}
n->choice.inodo.kidsNo = 2;
n->choice.inodo.children[0] =
n->choice.inodo.children[1];
// continua alla pagina seguente
35
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// continua i figli sono foglie e sono 3
// continua if (pos <= 1)
n->choice.inodo.children[1] =
n->choice.inodo.children[2];
res.old.size = 2;
res.old.max =
n->choice.inodo.children[1].child->choice.val;
res.old.child = n;
} else {
// pos >= 2
// continua alla pagina seguente
36
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// continua i figli sono foglie e sono 3
// pos >= 2
res.newEl = TORIGHT;
if (pos == 2) {
res.new = getNodeAndMakeHeader
(newHead,
n->choice.inodo.children[2]);
} else {
res.new
= getNodeAndMakeHeader
(n->choice.inodo.children[2],
newHead);
}
n->choice.inodo.kidsNo = 2;
res.old.size = 2;
res.old.max =
n->choice.inodo.children[1].child->choice.val;
res.old.child = n;
}
} // end if ha 2 o 3 figli
37
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
} else {
// i suoi figli non sono foglie
if (e <= n->choice.inodo.children[0].max) {
// si inserisce nel primo figlio
res = nodeInsert(n->choice.inodo.children[0].child,
e);
if (res.newEl == NONE) {
n->choice.inodo.children[0] = res.old;
setNONEResult(n, &(res.old));
// }
// continua alla pagina seguente
38
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
} else {
// continua i suoi figli non sono foglie
// continua si inserisce nel primo figlio
} else if(res.newEl == TOLEFT) {
if (n->choice.inodo.kidsNo == 2) {
n->choice.inodo.children[2] =
n->choice.inodo.children[1];
n->choice.inodo.children[1] = res.old;
n->choice.inodo.children[0] = res.new;
setNONEResult(n, &(res.old));
} else {
// kidsNo == 3 cioe’ split
// continua alla pagina seguente
39
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// continua i suoi figli non sono foglie
// continua si inserisce nel primo figlio
// kidsNo == 3 cioe’ split
n->choice.inodo.children[0] =
n->choice.inodo.children[1];
n->choice.inodo.children[1] =
n->choice.inodo.children[2];
n->choice.inodo.kidsNo = 2;
res.newEl = TOLEFT;
res.new = getNodeAndMakeHeader(res.new,
res.old);
setHeader2(n, &(res.old));
}
} else {
// continua alla pagina seguente
40
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// continua i suoi figli non sono foglie
// continua si inserisce nel primo figlio
// res.newEl == TORIGHT
if (n->choice.inodo.kidsNo == 2) {
n->choice.inodo.children[2] =
n->choice.inodo.children[1];
n->choice.inodo.children[0] = res.old;
n->choice.inodo.children[1] = res.new;
setNONEResult(n, &(res.old));
} else {
// continua alla pagina seguente
// kidsNo == 3 cioe’ split
41
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// continua i suoi figli non sono foglie
// continua si inserisce nel primo figlio
// continua res.newEl == TORIGHT
// kidsNo == 3 cioe’ split
n->choice.inodo.children[0] =
n->choice.inodo.children[1];
n->choice.inodo.children[1] =
n->choice.inodo.children[2];
n->choice.inodo.kidsNo = 2;
res.newEl = TOLEFT;
res.new = getNodeAndMakeHeader(res.old,
res.new);
setHeader2(n, &(res.old));
}
} // end if res
42
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
} else if (e <= n->choice.inodo.children[1].max
|| n->choice.inodo.kidsNo <= 2) {
// i suoi figli non sono foglie
// si inserisce nel secondo figlio
res = nodeInsert(n->choice.inodo.children[1].child,
e);
if (res.newEl == NONE) {
n->choice.inodo.children[1] = res.old;
setNONEResult(n, &(res.old));
} else if(res.newEl == TOLEFT) {
if (n->choice.inodo.kidsNo == 2) {
n->choice.inodo.kidsNo = 3;
n->choice.inodo.children[2] = res.old;
n->choice.inodo.children[1] = res.new;
setNONEResult(n, &(res.old));
} else {
// continua alla pagina seguente
43
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// continua i suoi figli non sono foglie
// continua si inserisce nel secondo figlio
// kidsNo == 3 cioe’ split
res.new = getNodeAndMakeHeader
(n->choice.inodo.children[0],
res.new);
n->choice.inodo.children[0] = res.old;
n->choice.inodo.children[1] =
n->choice.inodo.children[2];
n->choice.inodo.kidsNo = 2;
res.newEl = TOLEFT;
setHeader2(n, &(res.old));
}
} else {
// continua alla pagina seguente
44
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
// continua i suoi figli non sono foglie
// continua si inserisce nel secondo figlio
// res.newEl == TORIGHT
if (n->choice.inodo.kidsNo == 2) {
n->choice.inodo.kidsNo = 3;
n->choice.inodo.children[2] = res.new;
n->choice.inodo.children[1] = res.old;
setNONEResult(n, &(res.old));
} else { // kidsNo == 3 cioe’ split
res.newEl = TORIGHT;
n->choice.inodo.children[0] =
n->choice.inodo.children[1];
n->choice.inodo.children[1] = res.old;
n->choice.inodo.kidsNo = 2;
setHeader2(n, &(res.old));
res.new = getNodeAndMakeHeader
(res.new,
n->choice.inodo.children[2]);
45
} // end if res
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
} else {
// i suoi figli non sono foglie
// si inserisce nel terzo figlio
res = nodeInsert(n->choice.inodo.children[2].child,
e);
if (res.newEl == NONE) {
n->choice.inodo.children[2] = res.old;
setNONEResult(n, &(res.old));
} else {
// kidsNo == 3 cioe’ split
// continua alla pagina seguente
46
Inserzione di un elemento (key-based)
// continua struct result nodeInsert()
} else {
// i suoi figli non sono foglie
// si inserisce nel terzo figlio
// kidsNo == 3 cioe’ split
res.newEl = TORIGHT;
if (res.newEl == TOLEFT) {
res.new = getNodeAndMakeHeader(res.new,
res.old);
} else { // res.newEl == TORIGHT
res.new = getNodeAndMakeHeader(res.old,
res.new);
}
n->choice.inodo.kidsNo = 2;
setHeader2(n, &(res.old));
}
} // end in quale figlio si inserisce
}
return (res);
}
47
Esempio 5: rimozione di un elemento
1
• Supponiamo di volere rimuovere l'elemento di indice 5 (e5).
n0 - 12
n1 - 7
n3 - 2
n4 - 2
e1
e3
e2
n2 - 5
n5 - 3
e4
e5
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
48
Rimozione di un elemento
5.1
• Poiche' il nodo n5 aveva 3 figli esso rimane ancora con 2 figli, il
che e' legittimo.
• Ovviamente occorre aggiornare le informazioni strutturali
contenute in
• n5, che ora contiene 2 foglie,
• n1, che ora contiene 6 foglie, ed
• n0, che ora contiene 11 foglie.
• Notare che si sono dovute aggiornare le informazioni strutturali
solo nei nodi che appartengono al path tra la foglia rimossa e la
radice.
• Per cui la complessita' dell'operazione e' O(log(n))
49
Rimozione di un elemento
5.3
• Rimozione dell'elemento di indice 5 (e5).
n0 - 11
n1 - 6
n3 - 2
n4 - 2
e1
e3
e2
n2 - 5
n5 - 2
e4
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
50
Esempio 6: rimozione di un elemento
1
• Supponiamo di volere rimuovere l'elemento di indice 3 (e3).
n0 - 12
n1 - 7
n3 - 2
n4 - 2
e1
e3
e2
n2 - 5
n5 - 3
e4
e5
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
51
Rimozione di un elemento
6.2
• Poiche' il nodo n4 aveva solo 2 figli esso rimarrebbe ancora con solo
un figlio, il che e' illegittimo.
• Ma n4 ha almeno un fratello, e in particolare un fratello ad esso
adiacente (n3 non e' adiacente a n5)
• se uno dei fratelli adiacenti di n4 ha 3 figli, puo' cedere a n4 il figlio
adiacente
• nel nostro caso questo capita con n5, che puo' cedere a n4 il figlio
e5
• in questo modo n4 torna ad evere 2 figli, e quindi una
configurazione legittima.
• Ovviamente occorre aggiornare anche le informazioni strutturali
contenute in
• n1, che ora contiene 6 foglie, ed
• n0, che ora contiene 11 foglie.
• Notare che si sono dovute aggiornare le informazioni strutturali solo
nei nodi che appartengono al path tra la foglia rimossa e la radice.
• Per cui la complessita' dell'operazione e' O(log(n))
52
Rimozione di un elemento
6.3
• Rimozione dell'elemento di indice 3 (e3).
n0 - 11
n1 - 6
n3 - 2
e1
e2
n2 - 5
n4 - 2
n5 - 2
e4
e5
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
53
Esempio 7: rimozione di un elemento
1
• Supponiamo di volere rimuovere l'elemento di indice 2 (e2).
n0 - 12
n1 - 7
n3 - 2
n4 - 2
e1
e3
e2
n2 - 5
n5 - 3
e4
e5
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
54
Rimozione di un elemento
7.2
• Poiche' il nodo n3 aveva solo 2 figli esso rimarrebbe ancora con
solo un figlio, il che e' illegittimo.
• n3 ha si’ un fratello adiacente (n4), ma questo non ha un terzo figlio
da cedergli
• Avendo solo 2 figli, n4 puo' pero' assorbire l'unico figlio rimasto a
n3: questo porta alla scomparsa di n3.
• Poiche' il padre di n3 (n1) rimane comunque con 2 figli la
scomparsa di n3 non richiede altre ristrutturazioni nell'albero
• Ovviamente occorre aggiornare anche le informazioni strutturali
contenute in
• n1, che ora contiene 6 foglie, ed
• n0, che ora contiene 11 foglie.
• Notare che si sono dovute aggiornare le informazioni strutturali
solo nei nodi che appartengono al path tra la foglia rimossa e la
radice.
• Per cui la complessita' dell'operazione e' O(log(n))
55
Rimozione di un elemento
7.3
• Rimozione dell'elemento di indice 2 (e2).
n0 - 11
n1 - 6
n2 - 5
n4 - 3
e1
e3
n5 - 3
e4
e5
e6
n6 - 2
e7
e8
n7 - 3
e9
e10
e11
e12
56
Rimozione di un elemento
• Quando la rimozione di un nodo figlio non e' assorbibile con
trasferimento di figli dai nodi adiacenti, questo porta alla
scomparso del nodo padre.
• Ricorsivamente, la rimozione di un nodo puo' propagarsi lungo
l'albero fino alla radice
• Nel caso la radice si trovi ad avere un solo figlio essa deve
essere rimossa:
• il suo unico figlio diventa la radice dell'albero
• l'altezza dell'albero diminuisce di 1
57
BTree
• I 2-3 Btree rappresentano un caso particolare di k–(2k-1) BTree
• Il caso in cui k=2
• La possibilita' di scegliere un k opportuno e' fondamentale
quando la struttura di indiciamento deve essere realizzata su
disco
• in questo caso non occorre minimizzare solo le operazioni
ma anche gli accessi al disco
• Per rendere l'accesso piu' efficiente si possono anche distribuire
le informazioni finali sui nodi intermedi e non solo sulle foglie
• nota che, nel nostro caso, il valore di header.max in un
nodo non terminale di altezza 1 coincide con il valore
contenuto nella foglia associata
58