Range query su B-tree

annuncio pubblicitario
Range query su B-tree
F. d’Amore
4 luglio 2002
Problema
Dato un B-tree di ordine m, le cui chiavi sono numeri reali, e dato un intervallo [α, β] dell’asse reale,
progettare ed analizzare un algoritmo per l’individuazione di tutte le chiavi ξ ∈ [α, β] presenti in
B.
Soluzione
Algoritmo
Nella descrizione della soluzione di farà riferimento alla classe BTreeNode per la rappresentazione
Java dei nodi di un B-tree.
class BTreeNode {
int m;
boolean leaf;
int keyTally; // n.ro chiavi
int keys = new int[m-1];
BTreeNode references[] = new BTreeNode[m];
// metodi non riportati
}
L’algoritmo che verrà usato, BTreeRangeSearch, è una variante della visita DFS di un albero
m-ario, il cui schema generale di lavoro è il seguente:
1. visita (ricorsivamente) primo sottoalbero;
2. visita nodo;
3. visita (ricorsivamente) ordinatamente tutti i sottoalberi successivi al primo.
Nel caso specifico del B-tree di ordine m la visita DFS può essere cosı̀ descritta.
Algorithm BTreeDFS(BTreeNode v) {
if(v == null) return;
for(i = 0; i < v.keyTally; i++) {
BTreeDFS(v.references[i]);
<visita v.keys[i]>
}
BTreeDFS(v.references[v.keyTally]);
}
Si tratta, come è evidente, di una generalizzazione della visita DFS in ordine simmetrico.
L’algoritmo che usiamo per risolvere la range query è una semplice variante di BTreeDFS, in
cui la visita viene opportunamente “sfrondata” poiché non siamo interessati alle chiavi esterne
1
all’intervallo assegnato. In particolare, osserviamo che ad ogni sottoalbero Ts (sottoalbero avente
radice nel nodo s) è possibile associare un intervallo Is = [min(Ts ), max(Ts )], dove min(Ts ) e
max(Ts ) sono, rispettivamente la minima e la massima chiave presente nel sottoalbero Ts . È
chiaro che converrà visitare un sottoalbero Ts se e solo se Is e [α, β] non hanno intersezione
vuota (se hanno intersezione vuota sicuramente Ts non può contenere chiavi interne all’intervallo
[α, β]). Dunque, è opportuno effettuare tale controllo prima di iniziare la visita di un qualunque
sottoalbero.
Il problema pratico che nasce è ora: dato un nodo s, come posso ottenere Is ? In effetti, mentre
il calcolo esatto di Is può essere computazionalmente rilevante, è invece facile l’individuazione di
un intervallo (aperto) Is0 ⊃ Is , per tutti i nodi che abbiano un genitore: è sufficiente tener conto
della chiave o delle chiavi che nel genitore delimitano lo spazio attribuito al sottoalbero. Più precisamente, a causa della proprietà che rende “di ricerca” un B-tree, e dunque per costruzione, dato
un nodo v, l’intervallo I 0 associato al sottoalbero v.references[i], per 0 < i < v.keyTally,
è (v.keys[i-1],v.keys[i]); se i == 0 l’intervallo è (−∞,v.keys[0]); se i == v.keyTally
l’intervallo è (v.keys[v.keyTally-1],+∞). Chiaramente, ogni nodo s tale che Is0 ∩ [α, β] = ∅ è
radice di un sottoalbero che non contiene chiavi interessanti.
Algorithm BTreeRangeSearch(BTreeNode v, double alpha, double beta) {
if(v == null) return;
for(i = 0; i < v.keyTally; i++) {
if(v.keys[i] >= beta) {
BTreeRangeSearch(v.references[i], alpha, beta);
if(v.keys[i] == beta) <visita v.keys[i]>
halt; // termina l’elaborazione
} else if(v.keys[i] >= alpha) {
if(v.keys[i] > alpha) BTreeRangeSearch(v.references[i], alpha, beta);
<visita v.keys[i]>
}
}
/* se l’algoritmo non si e’ arrestato deve essere evidentemente
v.keys[i] > beta */
BTreeRangeSearch(v.references[v.keyTally]);
}
Analisi
Una prima analisi, ovvia ma corretta, ci consente di valutare il costo temporale dell’algoritmo in
O(2n/m) accessi al disco, in cui n è il numero delle chiavi ed m è l’arità dell’albero. Infatti, vengono
al più visitati tutti i nodi e il numero di nodi appartiene approssimativamente1 all’intervallo
[n/m, 2n/m].
Una valutazione più raffinata separa le componenti di costo, tenendo conto della modalità di
lavoro dell’algoritmo: dapprima la visita scende nell’albero, in pratica cercando la chiave alpha;
successivamente, appena trovata alpha o la prima chiave superiore ad alpha (ma non superiore a
beta), l’algoritmo comincia a riportare le chiavi nell’intervallo [alpha, beta], terminando appena
si incontra o si supera la chiave beta. Nella prima parte l’algoritmo spenderà al più un numero
di accessi al disco pari all’altezza dell’albero; nella seconda, se k chiavi rispondono alla query,
saranno spesi al più 2k/m (approssimativamente il numero max di nodi che contengono k chiavi
consecutive) accessi al disco.
In definitiva, O(logm/2 n) accessi nella prima parte e O(2k/m) accessi nella seconda. In totale,
O(logm/2 n + 2k/m) accessi al disco.
1 Si lascia al lettore l’esercizio di valutare con precisione la quantità massima e la quantità minima di nodi in un
B-tree di ordine m con n chiavi.
2
Spunti per esercizi
1. Valutare il costo dell’algoritmo BTreeRangeSearch in funzione di n (numero di chiavi) e di
B (dimensione del blocco), e non in funzione di m (arità).
2. Scrivere una variante dell’algoritmo BTreeRangeSearch in cui le chiavi richieste vengono
riportate in ordine inverso (dalla più grande alla più piccola).
3. Ipotizzando che le chiavi siano memorizzate all’interno dei nodi in array ordinati, modificare
l’algoritmo BTreeRangeSearch in maniera da sfruttare tale ordinamento, effettuando ricerche
binarie all’interno dei nodi. Come si modifica la complessità asintotica dell’algoritmo?
4. Modificare l’implementazione del B-tree prevedendo la memorizzazione esplicita, all’interno
di ciascun nodo, della massima e della minima chiave presenti nel sottoalbero avente tale
nodo come radice. Modificare opportunamente gli algoritmi di aggiornamento (inserimento
ed eliminazione) del B-tree. In presenza di tale implementazione di un B-tree come è possibile
semplificare l’algoritmo BTreeRangeSearch?
5. Risolvere il problema della range query su un BST.
3
Scarica