You are on page 1of 10

2.

7 Aibori B
Pina acum au fost studiate structurile arborescente de date care se pot reprezenta in memoria interna a calculatorului. Pentru reprezentarea lor s-au folosit structurile dinamice cu ajutorul pointerilor. Sint insa cazuri cind numrul de elemente este extrem de mare si nu poate sa incapa in intregime in memoria interna a calculatorului. In acest caz informaia se pstreaz pe un suport extern, de preferina pe discul magnetic, sub forma de fiiere. Daca se folosesc fiiere cu acces direct, legaturile intre articole sint asemntoare cu pointerii si toti algoritmii prezentai anterior, se pot pstra in forma in care au fost descrii. Dezavantajul consta in timpul foarte mare de acces la un articol din fiier, aducerea lui in memoria interna pentru prelucrare si transferarea lui inapoi pe disc. O cale noua de efectuare a operaiilor asupra structurilor arborescente a fost descoperita de R. Boyer, E. Mc.Creigent si M . Kaufman. Este vorba de ideea arborelui B , care face posibila atit cutarea ct si actualizarea unui fiier cu multa eficienta, folosind algoritmi relativ simpli.

Definiie:
Un B - arbore de ordinul 'm'este un arbore cu urmtoarele proprieti: 1) Fiecare nod are cel mult m ' descendeni. 2) Fiecare nod, exceptind rdcina si nodurile terminale, are cel puin (m / 2) descendeni. 3) Rdcina are cel cel puin doi descendeni, daca nu este si nod terminal. 4) Toate nodurile terminale apar pe acelai nivel si conin informaii. 5) Un nod care nu este terminal si are k descendeni, conine (k-1) chei.

Exemplu

Figura anterioara reprezint un B-arbore de ordinul 7. Fiecare nod cu excepia rdcinii si a nodurilor terminale conine intre 7 / 2 si 7 descendeni si deci poate avea 3 , 4 , 5 sau 6 chei. Toate nodurile terminale sint pe nivelul 3. Cheile apar in ordine cresctoare, folosind o extindere naturala a conceptului de ordine simetrica . De asemenea numrul nodurilor terminale este exact egal cu numrul de chei. Un nod 'p'care conine chei si (j + 1)adrese de referina se poate reprezenta astfel:

kl P l 2 ? 2

-pj-ikjPj

In acest caz k ( l ) <k(2) < . . . < k ( j ) cuprinse intre k(i) si k ( i + 1 ) .

, iar

p(i) fac referire la subarborele cu cheile

217

Vom numi in continuare un asemenea nod drept" pagina ".

Cutarea unei chei in B-arbori


Operaia de cutare intr-un asemenea arbore este o operaie directa . Succesiunea operaiilor este urmtoarea: - se aduce in memoria interna prima pagina din arbore - se efectueaz o operaie de cutare a cheii dorite pentru cheile k ( l ) . . . k(j) ale paginii - daca cutarea se termina cu succes, inseamna ca s-a gsit cheia dorita; daca nu s-a gsit cheia exista trei posibiliti: a) cheia cutata este intre k(i) si k(i+l);atunci se introduce in memorie pagina indicata de p(i); b) cheia cutata este mai mica decit k ( l ) se introduce in memorie pagina indicata de p(0); c) cheia indicata este mai mare decit k(j) atunci se va introduce in memorie pagina indicata e
P(j);

- in toate cele trei cazuri se reia procesul de cutare - daca se intilneste in procesul de cutare un p(i) care are valoarea "NIL" , cutarea in Barbore se termina fara succes. Cutarea unei chei date V printre cheile k ( l ) . . k(j) ale unei pagini poate sa fie o cutare secveniala pentru un numr mic de chei ( j ) sau una binara pentru un numr mare de chei. Pentru a putea descrie diferiii algoritmi afereni operaiilor ce se efectueaz asupra Barborilor, vom descrie structura unei pagini in acest arbore . Pentru aceasta, trebuie specificata , pentru fiecare pagina : -numrul de subarbori ai paginii respective ( j ' i n exemplul precedent) - pointerii spre fiecare rdcina a subarborelui (pagina) - cheile de cutare i n pagina . Notind cu "m" numrul maxim de descendeni vom avea urmtoarea declaraie de tip:
typedef s t r u c t pag { int nr; i n t key[m+1]; s t r u c t pag *nod[m+l]; } PPag;

Inserarea unei chei in B-arbori


Utilizarea B-arborilor reliefeaz uurina operaiilor de inserare. Operaia de inserare urmeaz unei operaii de cutare. Daca operaia de cutare se termina cu succes, cheia este gsita si nu mai este nevoie de inserare. In cazul cind operaia de cutare se termina fara succes, inseamna ca un nou nod se va introduce intr-o pagina terminala . Daca spre exemplu, se dorete inserarea unui nod cu cheia 337 , pagina urmtoare :

va f i modificata astfel:

218

307

313 ir

331
1'

337
1r

347

Se poate intimpla insa ca inserarea unui nod nou (o cheie noua) sa duca la depirea numrului maxim de chei admise pentru o pagina ( m ) . O metoda de rezolvare a acestei probleme este scindarea paginii in doua pagini astfel: din cele ( m + l)noduri rezultate, cel din mijloc va urca in pagina ascendenta, iar cele 'm'pagini ramase se scindeaz in doua , primele (m / 2) formind pagina din sting, ultimele (m 12) pagina din dreapta (scindare dup nodul median). Spre exemplu, daca in pagina din figura urmtoare:

31 97 )

Se dorete sa se insereze un nod cu cheia 71 , consideram ca numrul maxim de noduri este 6 ( m = 6 ), din cele 7 noduri (chei), nodul (cheia) din mijloc ( 67 ) urca in pagina superioara, iar pagina curenta se scindeaz astfel :

Daca in urma migrrii unui nod dintr-o pagina, in pagina ascendenta si aceasta din urma va conine mai mult de 'm' noduri se va repeta acelai procedeu. Daca este necesara si desprirea paginii rdcina , care nu are nici un ascendent, se va crea o noua pagina continind un singur nod cu cheia k (m/2). Se observa ca in acest mod B-arborii se vor dezvolta invers, adic de la nodurile terminale spre nodul rdcina. Algoritmul urmtor caut un nod cu cheia 'x'intr-un B-arborede rdcina " rad "; daca nu este gsit se insereaz un nou nod cu cheia V . Daca se depete numrul maxim de noduri pe pagina si un nod trebuie transferat la pagina rdcina , variabila booleana "h" este pusa pe "true" (arborele a devenit mai inalt). Procedura intoarce variabila h precum si pagina unde s-a gsit elementul cutat si indicele lui 'x'din pagina. De asemenea este un indicator daca va fi un element nou si se vor folosi procedurile "caut" pentru cutarea unui nod in arbore, "insert" pentm inserarea noului nod si "copie" pentru copierea unei pagini.
v o i d C o p i e ( p p a g * d e s t , p p a g * r , i n t i s , i n t i d , i n t n) /* copiaz informaia d i n p a g i n a " r " i n c e p i n d c u i n d i c e l e " i s " in pagina "dest" incepind cu i n d i c e l e " i d " */

{
w h i l e (n>0) { n--; dest->key[id+n] dest->nod [id+n]

= r->key [ i s + n ] ; = r->nod[is+n];

/* c h e i l e */ /* a d r e s e l e d e c e n d e n t i l o r

*/

}
} /* C o p i e */

219

void /*

Insert(ppag

* r , i n t k) " r " pe p o z i i a

insereaz e f e c t i v c h e i a "x" i n p a g i n a "k" d i n v e c t o r u l c h e i l o r */

{
int m_div_2, j ; = r->nr / 2; */

m_div_2 if

( r - > n r == m) { /* t r e b u i e f i s i o n a t a p a g i n a pv = (ppag * ) m a l l o c ( s i z e o f ( p p a g ) ) ; pv->nr = m_div_2; r->nr = m_div_2; i f (k > m _ d i v _ 2 ) { /* s e m i p a g i n a d r e a p t a */ i f (k == m _ d i v _ 2 + l ) { C o p i e ( p v , r , m _ d i v _ 2 + l , 1, m _ d i v _ 2 ) ; pv->nod[0] = p u r e ;

}
else { /* u r c a e l e m e n t u l k e y [ n + l ] */ k = k - m_div_2 - 1; C o p i e ( p v , r , m_div_2 + 2, 1, k - 1 ) ; pv->key[k] = u r c ; p v - > n o d [ k ] = pure,Copiepv, r , m _ d i v _ 2 + k + l , k+1, m _ d i v _ 2 - k ) ; pv->nod[0] = r->nod[m_div_2+l]; u r c = r->key[m_div_2 + l ] ;

}'
}
else { /* s e m i p a g i n a stnga C o p i e ( p v , r , m _ d i v _ 2 + l , 1, m _ d i v _ 2 ) ; pv->nod[0] = r->nod[m_div_2]; v = r->key[m_div_2]; C o p i e ( r , r , k, k+1, m _ d i v _ 2 - k ) ; r->nod[k] = pure; r->key[k] = u r c ; u r c = v; */

}
pure = pv; */

}
else { /* i n c a p e i n p a g i n a c u r e n t a h = 0; r - > n r = r - > n r + 1; C o p i e ( r , r , k, k+1, r - > n r - k ) ; r->nod[k] = pure; r->key[k] = u r c ; i f ( u r c == x ) { i n d = k; adr = r ;

}
} void /* /* I n s e r t */ Caut(ppag * r ) caut l o c u l c h e i i "x" i n p a g i n a c u r e n t a insereaz i n l o c u l corespunztor */ "r" s i se

{
int if k; ( r == NULL) h = 1; nou = 1; u r c = x; { /* nu a f o s t gsit */

220

pure

= NULL;

}
else { gsit = 0; w h i l e ( ( k <= r - > n r ) && igasit) { gsit = ( r - > k e y [ k ] >= x ) ; k++; } i f (gsit) k--; /* a f o s t i f (k > r - > n r ) gsit = 0; else gsit = ( r - > k e y [ k ] == x ) ; i f (gsit) { nou = 0; adr = r ; i n d = k; h = 0; /* cutam pe p a g i n a c u r e n t a */

gsit

*/

}
else { Caut(r->nod [k-1]) ; i f (h) I n s e r t ( r , k ) ; /* s e . c o n t i n u a cutarea */ /* s e insereaz k i n p a g i n a " r " */

} /* C a u t */ ppag * C a u t a ( i n t k e y ) /* i n s e f e z a c h e i a " k e y " c i t i t a i n a r b o r e l e "B" s i v a r e t u r n a p o i n t e r u l c o r e s p u n z t o r rdcinii */

{
x = key; /* caut i n c e p i n d d i n rdcina l o c u l de i n s e r a t a l cheii "x" */

Caut(rad); i f (h) { pv = (ppag * ) m a l l o c ( s i z e o f ( p p a g ) ) ; pv->nod[0] = r a d ; pv->nod[l] = pure; pv->key[l] = u r c ; p v - > n r = 1; r a d = pv;

}
if (x == u r c ) { adr - r a d ; i n d = 1;

}
return rad; } /* Caut Pentru */ funciile d i n C a u f o s t fcute u r m t o a r e l e declaraii

i n t i n d , u r c , v, x; i n t h, gsit, n o u ; p p a g * r a d , * a d r , * p u r c , *pv,-

Pentru tiprirea arborelui B pe nivele se da urmatorea procedura:


void Tiprire(ppag j ; ( r a d != NULL) { for ( j = l ; j<=nivel; *rad, i n t nivel)

{
int if

j++)

p r i n t f ( "" ) ;

221

for

( j = l ; j < = r a d - > n r ; j + + ) p r i n t f ( " % d ", r a d - > k e y [ j ] ) ; printf("\n"); gotoxy(20, whereyO); f o r ( j = 0 ; j<=rad->nr; j++) Tiprire(rad->nod[j], n i v e l + 1 ) ;

}
} /* Tiprire */

Exemplu Un exemplu de program ar putea f i urmtorul:


/* P r o g r a m de c r e a r e a a r b o r i l o r B p r i n inserie */ #include <stdio.h> # i n c l u d e <conio.h> #include <alloc.h> # i n c l u d e <ctype.h> #define m 4 /* declaraia d e t i p a a r b o r i l o r B */ typedef s t r u c t pag { int nr; i n t k e y [m+1] , s t r u c t pag *nod[m+l]; } ppag; /* v a r i a b i l e g l o b a l e utilizate p r i n funciile p r o g r a m u l u i */

t int^ikey,^ Nnod; char ch; y i n t i n d , / u r c , v, x; i n t h, gsit, n o u ; ppag * r a d , * a d r , * p u r c , *pv; void Copie(ppag * d e s t , p p a g * r , i n t i s , i n t i d , i n t n)

/* C o p i e */ void I n s e r t (ppag * r , i n t k) CJA I i '


x

/* I n s e r t

*/

<**^

~ ( *

v o i d C a u t (ppag * r )

/* C a u t */ ppag * C a u t a ( i n t k e y )

/* Caut */

/*

tiprete p e n i v e l e a r b o r e l e "B"

222

v o i d Tiprire(ppag

*p0, i n t n i v e l )

/* Tiprire */

ppag * I n s e r t N o d ( p p a g

*p0)

}
void { c l r s c r () Nnod = 0; nou = 1; r a d = NULL; do { p r i n t f ( " Creai n o d ? < Y / N > ") ch = t o u p p e r ( g e t c h O ) ; printf("\n"),"f > i f ( c h != 'N') { Nn6d++; p r i n t f ( " Introducei c h e i a : ") , s c a n f ( " % d " , &key); rad = Caut(key); mainO

}
} w h i l e ( c h != ' N ' ) ; clrscr() ; gotoxy(20, 5 ) ; p r i n t f ( " Arborele B este : " ) ; g o t o x y ( 2 0 , 10) ; Tiprire(rad, 0 ) ; gotoxy(1, 2 5 ) ; do { p r i n t f ( " Dorii [ I ] n s e r t i e s a u E [ x ] i t ch = toupper ( g e t c h O ) ; printf("\n"); gotoxy(wherex(), wherey()-1); delline(); switch(ch) { case ' I ' : r a d = InsertNod(rad); break; default : nothingO;

? ") ;

}
} while ( c h != 'X') ;

tergerea unei chei in B-arbori


tergerea este puin mai complicata dect inserarea. Paii algoritmului pentru aceasta operaie sunt: Pasl. Se incepe cutarea cheii de ters incepand cu rdcina. Pas2. Daca am gsit cheia in nodul curent avem doua subcazuri: a. Daca este vorba de un nod frunza se efectueaz tergerea; b. Altfel se inlocuieste cheia de ters cu succesoarea sa imediata, dupa care se terge 223

/
succesoarea. Pas3. Altfel se determina subarborele in care se poate gasi cheia de ters si se efectueaz tergerea in respectivul subarbore. Pas4. Daca in urma tergerii, un nod va avea mai puine chei dect minimul admis: a. Se imprumuta o cheie de la un nod frate, daca acest lucru este posibil (are de fapt loc o rotire a cheilor). b. Altfel, cele doua noduri fuzioneaz, imprumutand o cheie de la printe. In acest caz, printele poate ajunge la rndul lui la un numr de chei mai mic dect minimul permis, ceea ce duce la aplicarea recursiva a pasului 4. In urma acestei operaii, inaltimea arborelui poate scdea cu o unitate. Implementarea algoritmului este:
void deleteKey(ppag** r a d , i n t key) { p p a g * temp; deleteKey(*rad, k e y ) ; i f ( ( * r a d ) - > n r == 0) { //nu m a i avem n i c i o c h e i e temp = ( * r a d ) ; ( * r a d ) = ( * r a d ) - > n o d [0] , free(temp);

i n rdcina

Aceasta funcie incepe tergerea cu nodul rdcina, dupa care verifica daca inaltimea arborelui a fost modificata. tergerea efectiva o efectuam cu ajutorul funciei:
void d e l e t e K e y ( p p a g *nod, i n t k e y ) { i n t i = 1; int j ; i n t n r = nod->nr; ppag * c o p i l = NULL; ppag * c r e d i t o r ; i f (nod->key [ n r ] < k e y ) { / / c h e i a s e p o a t e g a s i numai i n u l t i m u l c o p i l a l n o d u l u i c o p i l = nod->nod[nr]; i = nr; i f ( c o p i l == NULL) { / / c h e i a cutata nu s e gsete i n a r b o r e return;

}
} else { / / c h e i a s e p o a t e g a s i i n nod s a u i n t r - u n u l d i n c o p i i while (key > nod->key[i]) { i + +;

}
if ( k e y == n o d - > k e y [ i ] ) { //am g a s i t - o i n n o d u l a c e s t a i f ( n o d - > n o d [ i ] == NULL) { / / e s t e un nod f r u n z a f o r (; i < n r ; i + + ) { //mutam t o a t e c h e i l e d i n d r e a p t a poziie nod->key[i] = nod->key[i + 11;

celei

terse,

spre

stnga

cu o

}
nod->nr--; //nu m a i avem n i m i c de fcut l a a c e s t return; nivel;

224

} else { / / c o n t i n u a m pe p r i m u l s u b a r b o r e r e t u r n findMinimum(nod->nod[0] ) ;

Exemplu
Presupunem ca avem urmtorul arbore,de ordinul 5:

Pentru tergerea cheii 14 se efectueaz urmtorii pasi: 1. Se inlocuieste cheia 14 cu succesoarea sa, cheia 16. 2. Se terge cheia 16. 3. In urma tergerii nodul frunza devine subdimensionat, asa ca se imprumuta o cheie de la frunza din stnga. De fapt, cheia imprumutata urca in printe, coborandu-se o cheie din nodul printe. Dupa tergerea cheii 14, arborele va avea urmtoarea forma:

UH1U

tergerea cheii 12 va avea efecte radicale asupra arborelui:

227

Etapele sunt: 1. Se inlocuieste cheia 12 cu succesoarea sa, cheia 16. 2. Se terge cheia 16. 3. In urma tergerii, nodul ramane cu o singura cheie, 17. Deoarece nodul din stnga nu are dect doua chei se relizeaza fuziunea, imprumutandu-se o cheie de la printe, rezultnd un nod format din cheile: 10,11,16,17. 4. In urma imprumutului a rezultat un nod avnd sub numrul admis de copii. Deoarece si nodul din dreapta (25, 33) are numrul minim de chei se realizeaz fuziunea. In urma acestei fuziuni, rdcina ramane fara singura cheie, noua rdcina fiind nodul creat la acest pas.

Probleme propuse:
1. Folosind procedura de cutare si inserare a unui element i n B-arbore sa se scrie un program care creaza un B-arbore apoi listeaz toate elementele ale cror chei sint curinse intre doua valori date. 2. Un arbore T se numete arbore B de ordin m ' daca este un arbore m-nar de cutare care ori este vid ori are inaltimea mai mare sau egala cu 1 si satisface urmtoarele proprieti: (i) Nodul rdcina are cel puin 2 copii; (ii) Toate nodurile diferite de rdcina si de nodurile de esec (un nod de esec are aceiai semnificaie ca la arborii binari de cutare, fiind un nod la care se poate ajunge in urma unei cutri daca valoarea cutata nu este in arbore) au cel puin hi/2 | f i i ; (iii) Toate nodurile de esec se afla pe acelai nivel. Se cere: a) Sa se arate ca arborele urmtor:

este un arbore B de ordin m", identificindu-se m"; b) Sa se defineasc o structura de date pentru reprezentarea arborilor B de ordin ' m ' ; c) Presupunem ca dorim sa inseram un nou nod intr-un arbore B de ordinul 'm'. In urma unei 228