Arbori, definitii proprietati

  • Upload
    nicutds

  • View
    170

  • Download
    4

Embed Size (px)

Citation preview

13. ARBORI B 13.1 Arbori B. Definiie. Proprieti. n cazul sistemelor de gestiune a bazelor de date relaionale (SGBDR) esteimportantcapelngstocareadatelorsserealizezeiregsirea rapidaacestora.nacestscopsuntfolosiiindecii.Unindexesteo coleciedeperechi.Scopulprimaralunui indexesteaceladeafacilitaaccesullaocoleciedearticole.Unindexse spunecaestedensdacelconinecteoperechepentrufiecarearticoldincolecie.Unindexcarenuestedens uneori este numit index rar. Structura de date foarte des folosit pentru implementarea indecilor este arborele de cutare. Articolele memorate pot fi orict de complexe, dar eleconinuncmpnumitcheieceservetelaidentificareaacestora.S notmcuCmulimeacheilorposibilecevortrebuiregsitecuajutorul arboreluidecutare.Dacarboreledecutareesteastfelconstruitnct foloseteorelaiedeordinetotalpeC,atuncivomspunecarborelede cutare este bazat pe ordinea cheilor. Arborii de cutare, bazai pe ordinea cheilor,suntdedoufeluri:arboribinaridecutare(auosingurcheie asociatfiecruinod)sauarborimulticidecutare(aumaimultechei asociate fiecrui nod). Performaneleunuiindexsembuntescnmodsemnificativprin mrireafactoruluideramificareaarboreluidecutarefolosit.Arborii multici de cutare sunt o generalizare a arborilor binari decutare. Astfel, unuinodoarecare,nlocsiseataezeosingurcheiecarepermite ramificareandoisubarbori,iseataeazunnumrdemchei,ordonate strictcresctor,carepermitramificareanm+1subarbori.Numrulm difer de la nod la nod, dar n general pentru fiecare nod trebuie s fie ntre anumite limite (ceea ce va asigura folosirea eficient a mediului de stocare). Cele m chei ataate unui nod formeaz o pagin. Determinarea poziiei cheii cutatencadrulunuinodserealizeazsecvenialncazulpaginilorcu numr mic de chei sau prin cutare binar. Un exemplu de arbore multici de cutare de ordin 3 este dat n figura 13.1. 50 100 20 40 120 140 7015 30 42 43 60 65 110 130136150 718263669 62 58145 Fi gur a 13. 1 Arbore multici de cutare de ordin 3 Unarboremulticidecutarecarenuestevidareurmtoarele proprieti: -fiecare nod al arborelui are structura dat n figura 13.2; nP0K0P1K1P2 Pn- 1 Kn - 1 Pn Fi gur a 13. 2 Structura de nod pentru un arbore multici de cutarede ordin n unde P0, P1, , Pn sunt pointeri ctre subarbori i K0, K1, , Kn 1 suntvalorilecheilor.Cerinacafiecarenodsaibunnumrde ramificaii mai mic sau egal dect m conduce la restricia n m 1. -valorile cheilor ntr-un nod sunt date n ordine cresctoare: Ki < Ki + 1, 2 , 0 = n i (13.1) -toatevaloriledecheidinnodurilesubarboreluiindicatdePisunt mai mici dect valoarea cheii Ki, 1 , 0 = n i . -toatevaloriledecheidinnodurilesubarboreluiindicatdePnsunt mai mari dect valoarea de cheie Kn 1. -subarborii indicai de Pi, n i , 0 =sunt de asemenea arbori multici de cutare. PrimaoararboreleBafostdescrisdeR.BayeriE.McCreightn 1972.ArboriiBrezolvproblemelemajorentlnitelaimplementarea arborilor de cutare stocai pe disc: -auntotdeaunatoatenodurilefrunzpeacelainivel(cualte cuvinte sunt echilibrai dup nlime); -operaiile de cutare i actualizare afecteaz puin blocuri pe disc; -pstreaz articolele asemntoare n acelai bloc pe disc; -garanteazcafiecarenoddinarborevafiplincuunprocent minim garantat. UnarboreBdeordinmesteunarboremulticidecutareiare urmtoarele proprieti: i.toate nodurile frunz sunt pe acelai nivel; ii.rdcina are cel puin doi descendeni, dac nu este frunz; iii.fiecarepaginconinecelpuin (((

2mchei(excepiefacerdcina care poate avea mai puine chei, dac este frunz); iv.nodulestefiefrunz,fiearen+1descendeni(undeneste numrul de chei din nodul respectiv, cu (((

2m n m-1 ); v.fiecare pagin conine cel mult m-1 chei; din acest motiv, un nod poate avea maxim m descendeni. Proprietateaimeninearborelebalansat.Proprietateaiiforeaz arborele s se ramifice devreme. Proprietatea iii ne asigur c fiecare nod al arborelui este cel puin pe jumtate plin. nlimeamaximaunuiarboreBdmargineasuperioara numrului de accese la disc necesare pentru a localiza o cheie. SeconsiderhnlimeamaximaunuiarboreBcuNchei,unde valoarea indicatorului este dat de relaia: |.|

\| +=(((

21log2Nhm(13.2) Caexemplu,pentruN=2.000.000im=20,nlimeamaxima unui arbore B de ordin m va fi 3, pe cnd un arborele binar corespondent va avea o nlime mai mare de 20. 13.2 Operaii de baz ntr-un arbore B Procesul de cutare ntr-un arbore B este o extindere a cutrii ntr-un arbore binar. Operaia de cutare n arborele B se realizeaz comparnd cheia cutat x cu cheile nodului curent, plecnd de la nodul rdcin. Dac nodul curent are n chei, atunci se disting urmtoarele cazuri: -ci < x < ci +1,n i , 1 = se continu cutarea n nodul indicat de Pi; -cn < x se continu cutarea n nodul indicat de Pn; -x < c0 se continu cutarea n nodul indicat de P0. Lungimeamaximadrumuluidecutareestedatdenlimea arborelui. Fiecare referire a unui nod implic selecia unui subarbore. ArboreleBsuportcutareasecvenialacheilor.Arboreleeste traversat secvenial prin referirea n inordine a nodurilor. Un nod este referit demaimulteorintructelconinemaimultechei.Subarboreleasociat fiecrei chei este referit nainte ca urmtoarea cheie sa fie accesata. Arborii B sunt optimi pentru accesul direct la o cheie. Pentru accesul secvenial la o cheie nu se obin performane satisfctoare. Condiiacatoatefrunzelesfiepeacelainivelducelaun comportamentcaracteristicarborilorB:fadearboriibinaridecutare, arborilor B nu le este permis s creasc la frunze; ei sunt forai sa creasc la rdcin. OperaiadeinserareauneicheinarboreleBesteprecedatde operaiadecutare.ncazuluneicutricusucces(cheiaafostgsitn arbore)nusemaipuneproblemainserriintructcheiaseafladejan arbore. Dac cheia nu a fost gsit, operaia de cutare se va termina ntr-unnodfrunz.nacestnodfrunzsevainseranouacheie.Funciede gradul de umplere al nodului frunz afectat, se disting urmtoarele cazuri: -nodul are mai puin de m 1 chei; inserarea se efectueaz fr s se modifice structura arborelui ; -nodularedejanumrulmaximdem1chei;nurmainserrii nodulvaaveapreamultechei,deaceeaelvafisiona.nurma fisionrii vom obine dou noduri care se vor gsi pe acelai nivel iocheiemediancarenusevamaigsinniciunuldincele dounoduri.Cele (((

2mcheidinstngarmnnnodulcare fisioneaz.Cele (((

2mdindreaptavorformacelde-aldoileanod. Cheia median va urca n nodul printe, care la rndul lui poate s Se observ c procesul de inserare a unei chei garanteaz c fiecare nodinternvaaveacelpuinjumtatedinnumrulmaximdedescendeni. n urma operaiilor de inserare arborele va deveni mai nalt i mai lat, figura 13.3. Fi gur a 13. 3 Modificrile dimensionale ale unui arbore Bdup efectuarea inserrii S considerm un arbore B de ordin 5 (deci numrul maxim de chei dintr-unnodvafi4).nurmainserriivalorilordecheie22,57,41,59 nodul rdcin va fi cel din figura 13.4. 22415759 Fi gur a 13. 4 Structura nodului rdcin dup inserarea cheilor n urma inserrii cheii 54, nodul rdcin va conine prea multe chei, aa c el va fisiona, figura 13.5. 224154575922415759 cheia mediana 54 va promova Fi gur a 13. 5 Fisionarea nodului rdcin nurmapromovriicheiimediene54sevaformaonourdcin, arborele crescnd n nlime cu 1, figura 13.6. 54 2241 57 59a bc Fi gur a 13. 6 Formarea noii rdcini a arborelui Inserarea cheilor 33, 75, 124 nu ridic probleme, figura 13.7. 54 2233 57 59 4175 124 a bc Fi gur a 13. 7 Structura arborelui dup inserarea cheilor 33, 75, 124 Inserarea cheii 62 ns duce la divizarea nodului c, figura 13.8. 57 5962 75 c 12457 59 75 124 Cheia mediana 62 va promova cd Fi gur a 13. 8 Fisionarea nodului c Cheia 62 va promova n nodul rdcin, figura 13.9. 54 224157 596275 124 b c d a Fi gur a 13. 9 Promovarea cheii 62 n rdcin n urma inserrii cheilor 33, 122, 123, 55, 60, 45, 66, 35 configuraia arborelui va fi cea din figura 13.10. 54 35122 622233414555 57 59 60 66 75 123124 b a fd ec Fi gur a 13. 10 Structura arborelui dup inserri succesive Inserareacheii56sevafacennodulciacestavafisiona,figura 13.11. 55 5657 59 c 60 55 56 5960Cheia mediana 57 va promova cg Fi gur a 13. 11 Fisionarea nodului c Oricum, nodul printe a este deja plin i nu poate primi noua cheie 57 i pointerul ctre nodul nou format f. Algoritmul de fisionare este aplicat din nou, dar de data aceasta nodului a, figura 13.12. 355457 62 a 12235 54 62 122 Cheia mediana 57 va promova ah Fi gur a 13. 12 Fisionarea nodului a Cheiamedianpromovat57vaformanouardcinaarboreluiB, avnd subarborele stng a i subarborele drept h, figura 13.13. 57i 355462122 223341455556 59 60 66 75123124 b a h fcgde Fi gur a 13. 13 Noua configuraie a arborelui B Uzual,nspecialpentruarboriBdeordinmare,unnodprinteare suficientspaiudisponibilpentruaprimivaloareauneicheiiunpointer ctreunnoddescendent.ncelmairucazalgoritmuldefisionareeste aplicat pe ntreaga nlime a arborelui. n acest mod arborele va crete n nlime, lungimea drumului de cutare crescnd cu 1. Sintetizat, algoritmul de inserare a unei valori de cheie n arbore este prezentat n figura 13.14. Adaugnouavaloare decheiennodul frunz corespunzator OVERFLOW?Dividenodulndounoduri aflatepeacelainiveli promoveaz cheia median Fi gur a 13. 14 Algoritmul de inserare a unei chei n arbore Algoritmul de inserare ntr-un arbore B (pseudocod): -insereaz noua valoare de cheie n nodul frunz corespunztor; -nodul_curent = nodul_frunza; -while( starea pentru nodul_curent este OVERFLOW ): -dividenodul_curentndounoduriaflatepeacelainiveli promoveazcheiamediannnodulprintepentru nodul_curent; -nodul_curent = nodul printe pentru nodul_curent. ncelmairucaz,inserareauneicheinoiducelaaplicarea algoritmului de fisionare pe ntreaga nlime a arborelui, fisionndu-se h 1 noduri, unde h este nlimea arborelui nainte de inserare. Numrul total de fisionri care au aprut cnd arborele avea p noduri este de p 2. Prima fisionareadaugdounodurinoi;toatecelelaltefisionriproducdoarun singurnodnou.Fiecarenodareunminimde12(((

mchei,cuexcepia rdcinii,carearecelpuinocheie.Deciarborelecupnoduriconinecel puin( )||.|

\|(((

+ 121 1mp chei.Probabilitateacaofisionaresfienecesar dup inserarea unei valori de cheie este mai mic dect : ( )cheidivizari(13.3) ( )||.|

\|(((

+121 12mpp

careestemaimicdect1divizareper12(((

minserridechei(deoarece 21 p tinde ctre 0 pentru o valoare mare a lui p i 21pp este aproximativ 1). De exemplu, pentru m = 10, probabilitatea apariiei unei divizri este de 0.25. Pentru m = 100, probabilitatea divizrii este 0.0204. Pentru m = 200, probabilitatea divizrii este 0.0101. Cu alte cuvinte, cu ct ordinul arboreluieste mai mare, cu att este mai mic probabilitatea ca inserarea unei valori de cheie sa duc la divizarea unui nod. Operaiadetergeredintr-unarboreBestecevamaicomplicat dectoperaiadeinserare.Operaiadetergereserealizeazsimpludac valoarea de cheie care urmeaz a fi tears se afl ntr-un nod frunz. Dac nu, cheia va fi tears logic, fiind nlocuit cu o alta, vecin n inordine, care va fi tears efectiv. n urma tergerii se disting urmtoarele cazuri: -dacnodulconinemaimultde (((

2mchei,tergereanuridic probleme; -dacnodularenumrulminimdechei (((

2m,duptergere numrul de chei din nod va fi insuficient. De aceea se mprumut ocheiedinnodulvecin(aflatpeacelainivelnarbore)dac acestaarecelpuin (((

2mchei,cazncareavemde-afacecuo partajare.Dacnusepoatefaceopartajarecuniciunuldin SconsidermurmtoareaconfiguraiedearboreBdeordin5din figura 13.15. 57a 355462122 2233 4041 5556 59 60 66 75123124 d bc e fghi 22 4550 Fi gur a 13. 15 Arbore B de ordin 5 tergerea valorilor de cheie 40 i 45 din nodul e nu ridic probleme, figura 13.16 57a 355462122 2233415055 56 59 60 66 75123124 d bc efghi 34 Fi gur a 13. 16 Structura arborelui dup tergerea cheilor 40 i 45 tergerea valorii de cheie 50 necesit ns partajarea ntre nodurile d i e, figura 13.17. 57a 3554 62122 2233415055 56 59 60 66 75 123124 d bc efghi 34valoareadecheie35 coboara;valoareade cheie 34 urca Fi gur a 13. 17 Partajarea ntre nodurile d i e 57a 3454 62122 2233354155 56 59 60 66 75 123124 d bc efghi Fi gur a 13. 18 Structura arborelui dup partajare tergerea valorii de cheie 22 va necesita fuzionarea nodurilor d i e, figura 13.19. fuzionare 57a 3454 62 122 22 3335 4155 56 59 60 66 75 123 124 d bc efghi Fi gur a 13. 19 Fuzionarea nodurilor d i e nsnurmafuzionriinodurilordie,nodulbvaconineprea puine valori de cheie, aa c vor fuziona i nodurile b i c, figura 13.20. 57a 5462122 33343541 5556 59 60 66 75123124 d bc fghi fuzionare Fi gur a 13. 20 Fuzionarea nodurilor a, b i c Astfel, n final, arborele B va arta astfel ca n figura 13.21. 5457 62122 333435415556 59 60 66 75123124 Fi gur a 13. 21 Structura final a arborelui B Algoritmul de tergere dintr-un arbore B, n pseudocod: -if(valoareadecheiecaresetergenuestentr-unnodfrunz) then: nlocuiete valoarea de cheie cu succesor / predecesor; -nodul_curent = nodul_frunza; -while (starea pentru nodul_curent este UNDERFLOW ): -ncearc partajarea cu unul din nodurile vecine aflate pe acelai nivel, via nodul printe; -if (nu este posibil ) then: 1.fuzioneaz nodul_curent cu un nod vecin, folosind o valoare de cheie din nodul printe; 2.nodul_curent = printele pentru nodul_curent. 13.3 Algoritmii C++ pentru inserarea unei valori de cheie ntr-un arbore B naintedeaprezentaalgoritmiipentrucutareiinserarentr-un arboreB,sncepemmainticudeclaraiilenecesarepentru implementarea unui arbore. Pentru simplicitate vom construi ntregul arbore Bnmemoriaheap,folosindpointeripentruadescriestructura arborescent.nmajoritateaaplicaiilor,acetipointerivortrebuinlocuii cuadresectreblocurisaupaginistocatentr-unfiierpedisc(deci accesarea unui pointer va nsemna un acces la disc). Un nod al arborelui B va fi descris de clasa btree_node. Pentru a fi ct maigeneralnceeaceprivetetipulvaloriidecheiefolositvafi implementatcafiindoclastemplate.Oricum,tipulvaloriidecheie folositvatrebuispermitordonareacheilor;ncazulimplementriide facerinaestedeasuportatesteledeinegalitatestrict(node_capacity = capacity ; initialize( this ); } template int btree_node::initialize( btree_node_ptr node ) { if( 0 = = node ) return -1 ; node->node_keys = new key_node_type[ node->capacity() + 1 ]; if( 0 = = node->node_keys ) return -1 ; node->node_childs = new btree_node_ptr[ node->capacity() + 2 ]; if( 0 = = node->node_childs ) { delete []node->node_keys ; node->node_keys = 0; return -1 ; } memset(node->node_childs,0,sizeof(btree_node_ptr)* (node->capacity() + 2 ) ); node->node_size = 0; return 0; } Destructorulbtree_node::~btree_node()elibereaz memoria alocat: template btree_node::~btree_node( void ) { delete []this->node_keys ; delete []this->node_childs ; } Pentru a se afla numrul de chei aflate la un moment dat ntr-un nod se folosete funcia size(): template inline int btree_node::size( void ) const { return this->node_size ; } Pentruasedeterminanumrulmaximdecheicarepotfipstrate ntr-un nod se folosete funcia capacity(): template inline intbtree_node::capacity( void ) const{ return this->node_capacity ; } Un nod poate fi interogat pentru starea n care se afl folosind funcia status(): template inline btree_node::BTREE_NODE_STATUSbtree_node::status( void ) const {if( 0 == size() ) return EMPTY ; else if( size() < ( capacity() / 2 ) ) return UNDERFLOW ; else if( size() == ( capacity() / 2 ) ) return MINIMAL ; else if( size() == capacity() ) return FULL ; else if( size() > capacity() ) return OVERFLOW ; return OPERATIONAL ; } Se observ c starea nodului este funcie de numrul de chei aflate la unmomentdatnnod.Maiexact,dacavemunarboreBdeordinm, atuncinumrulmaximdecheidintr-unnodvafim1,iarnumrul minimdecheivafi12(((

m.Parametrulprimitdeconstructorvafim1, decicapacitateanoduluivafim1.Cumnumruldecheiaflatelaun moment dat ntr-un nod se obine folosind funcia size(), vom avea: -dacsize()returneazzero,atuncinnodnusegsetenicio cheiestarea nodului este EMPTY ; -numrulminimdecheidinnodestecapacity()/2;decidac size()==capacity()/2,atuncinodularenumrulminimdechei starea nodului este MINIMAL ; -dacsize()capacity(),atuncinodularepreamultechei starea nodului este OVERFLOW. Pentruasedeterminadacnodulesteunnodfrunzsefolosete funcia is_leaf(), care va returna true dac nodul este o frunz (un nod este nod frunz dac nu are nici un descendent): template bool btree_node::is_leaf( void ) const { assert( 0 != this->node_childs ) ; // return 0 == this->node_childs[0] ; for( int idx = 0; idx node_childs[idx] ) return false ; return true ; } Cafunciipentruinterogareavaloriiuneicheiiunuidescendent dintr-o anume poziie se folosesc funciile key_value() i child(): template inline KEY_NODE_TYPE&btree_node::key_value( int position ) { if( position < 0 || position >= size() ) { /**signal out of bounds*/ assert( false ) ; } return this->node_keys[position] ; } template inline btree_node* btree_node::child( int position ) { if( position < 0 || position > size() ) { /**signal out of bounds*/ assert( false ) ; return 0 ; } return this->node_childs[position] ; } Cutareauneivaloridecheientr-unnodsefacefolosindfuncia find().Primulparametruprimitdefuncieestevaloareadecheiecarese caut n nod. Dup cum valoarea de cheie cutat se gsete sau nu n nod, find()vareturnatruesaufalse,cumeniuneacaaldoileaparametrual funciei(position,careesteunparametrudeieire)vafisetatdupcum urmeaz: -daca valoarea de cheie cutat se gsete n nod, atunci position este setat la indexul la care se gsete cheia n nod; -dacvaloareadecheiecutatnusegsetennod, positionva indicaindexulsubarboreluincares-arputeagsivaloareade cheie cutat. template bool btree_node::find( const key_node_type& value,int& position ) { bool ret_value ; position = -1 ; if( _less_than( value, this->node_keys[0] ) ) { position = 0 ; ret_value = false ; } else { for( position = size() - 1 ; _less_than( value, key_value( position ) ) && position > 0; position-- ) ; ret_value = _equal_to( value, this->node_keys[position] ) ; if( !ret_value ) position++ ; } return ret_value ; } Inserareauneivaloridecheiennodserealizeazfolosindfuncia push().nimplementareadefainserareavaloriidecheientr-unnodse facentotdeaunaspecificndipointerulctresubarboreledindreapta valoriidecheie(subarborecareconinetoatevaloriledecheiemaimari dect valoarea de cheie inserat). Dac nodul este n starea OVERFLOW sau valoarea de cheie exist deja n nod, funcia va returna 1, fr a face nimic altceva.nurmadeterminriipoziieipecarevafiinseratvaloareade cheie, ar putea fi necesar o deplasare ctre dreapta a valorilor de cheie din nod mai mari dect cea care se insereaz (deplasarea se face mpreun cu pointerii ctre descendeni; funcia folosit este shift2right()). template int btree_node::push(const key_node_type&value,btree_node_ptrchild) { if( OVERFLOW == status() ) return -1 ; if( EMPTY == status() ) { this->node_keys[0] = value ; this->node_childs[1] = child ; this->node_size = 1 ; return 0 ; } int key_position = -1 ; if( find( value, key_position ) ) { /**duplicate key value*/ return -1 ; } if( key_position < size() ) shift2right( key_position ) ; this->node_keys[key_position] = value ; this->node_childs[key_position + 1] = child ; this->node_size++ ; return 0 ; } Funcia shift2right() este: template int btree_node::shift2right( const int start ) { if( EMPTY == status() || start = size()) return -1 ; for( int idx = size(); idx > start; idx-- ) { this->node_keys[idx] = this->node_keys[idx - 1] ; this->node_childs[idx + 1] = this->node_childs[idx] ; } return 0 ; } Pentru eliminarea unei chei dintr-o anumit poziie se va folosi funcia remove_at() (practic, eliminarea presupune diminuarea cu unu a numrului de chei coninute de nod i o deplasare ctre stnga a valorilor de cheie i a subarborilor aflai n dreapta poziiei din care se terge cheia). template int btree_node::remove_at( const int position ) { if( -1 == shift2left( position ) ) return -1 ; this->node_size-- ; return 0 ; } Funcia shift2left() este: template int btree_node::shift2left( const int start ) { if( EMPTY == status() || start < 0 || start >= size() ) return -1 ; for( int idx = start + 1; idx < size(); idx++ ) { this->node_keys[idx - 1] = this->node_keys[idx] ; this->node_childs[idx] = this->node_childs[idx + 1] ; } return 0 ; } Atunci cnd starea unui nod este de OVERFLOW, acesta se va diviza. nurmadivizriisevaobineunnodnou.Funciadedivizareaunuinod estesplit().Parametrulfuncieisplit()estedeieire,fiinddetipul btree_node_tuple: typedefstruct { key_node_type key_value ; btree_node_ptr node_ptr ; } btree_node_tuple ; nurmaprocesuluidedivizareaunuinodvaurcannodulprinte cheiamedianiunpointerctrenodulnouformat.Cheiamedianvafi cmpulkey_value al structurii btree_node_tuple. Pointerul ctre nodul nou formatvaficmpulnode_ptralstructuriibtree_node_tuple.Noulnodva conine valorile de chei i subarborii din dreapta cheii mediane. template int btree_node::split(btree_node::btree_node_tuple*tuple) { if( 0 == tuple ) return 0 ; if( OVERFLOW != this->status() ) return 0; int median_position = this->size() / 2; tuple->key_value = this->key_value( median_position ); btree_node_ptr new_node = new btree_node_type( this->capacity()); if( 0 == new_node ) return -1; for( int idx = median_position + 1; idx < this->size() ; idx++ ) new_node->push( this->key_value( idx ), this->child( idx + 1 ) ); new_node->node_childs[0] = this->child( median_position + 1 ); this->node_size = median_position; tuple->node_ptr = new_node; return 0 ; } n continuare, va fi dat declaraia clasei de arbore B: template class btree { public: typedef KEY_TYPEkey_type ;typedef btree_nodebtree_node_type ; typedef btree_node_type*btree_node_ptr ; typedef btree_node_type::btree_node_tuple btree_node_tuple ; typedef btreebtree_type ; typedef btree_type*btree_ptr ; public: btree( int order ) ; virtual ~btree( void ) ; int push( const key_type& value ) ; int remove( const key_type& value ) ; protected: int push_down( btree_node_tuple* tuple, btree_node_ptr current ) ; int remove_down( const key_type& value, btree_node_ptr current ) ; int replace_with_predecessor( btree_node_ptr node, int position ) ; void restore( btree_node_ptr current, const int position ) ; void move_left( btree_node_ptr current, const int position ) ; void move_right( btree_node_ptr current, const int position ) ; void combine( btree_node_ptr current, const int position ) ; private: btree_node_ptrroot ; intorder ; } ; /**class btree */ Clasa btree este o clas template dup tipul valorii de cheie. La fel ca laclasabtree_nodeaufostfolositeoseriedetypedef-urincadrulclasei (codulestemaiuordescris/cititdactipulbtree_nodese redefinetecafiindbtree_node_type).NodulrdcinalarboreluiBeste dat de cmpul root. Ordinul arborelui B este dat de cmpul order.Constructorulbtree::btree()primetecaparametru ordinul arborelui i seteaz rdcina arborelui la 0. template btree::btree( int order ) { this->order = order ; this->root= 0 ; } Destructorul clasei btree::~btree() va elibera spaiul de memorie ocupat de arbore. template btree::~btree( void ) { clear( this->root ) ; this->root = 0 ; } template void btree::clear( btree_node_ptr node ) { if( 0 == node ) return ; if( !node->is_leaf() )for( int idx = 0 ; idx size(); idx++ ) clear( node->child( idx ) ) ; delete node ; } Funcia de inserare a unei valori de cheie n arbore este push(). Dac arborelenuareniciovaloaredecheieinserat(adicrdcinaarborelui este 0arborele este gol), atunci inserarea valorii de cheie este simpl: se construiete rdcina arborelui cu valoarea de cheie care se insereaz. Daca arborelenuestegol,atunciseinsereazcheianarborerecursivfolosind funcia push_down(), pornind de la nodul rdcin. Funcia push_down() va returna1dacnurmaprocesuluideinserarerecursivavaloriidecheie nodul rdcin a fost divizat (cheia i un pointer ctre subarborele drept al cheiivorficoninutedecmpurilevariabileituple).nacestcaznlimea arborelui crete cu unu, formndu-se o nou rdcin cu valoarea de cheie dat de cmpul kei_value al variabilei tuple; subarborele stng va fi vechea rdcin a arborelui, iar subarborele drept va fi dat de cmpul node_ptr al variabilei tuple. template int btree::push( const key_type& value ) { if( 0 == this->root ) { this->root = new btree_node_type( this->order - 1 ) ; if( 0 == this->root ) return -1 ; this->root->push( value, (btree_node_ptr)0 ) ; return 0 ; } btree_node_tuple tuple ; tuple.key_value = value ; tuple.node_ptr = 0 ; if( push_down( &tuple, this->root ) ) { btree_node_ptr new_root = new btree_node_type( this->order - 1 ); if( 0 == new_root ) return -1 ; new_root->push( tuple.key_value, tuple.node_ptr ) ; new_root->node_childs[0] = this->root ; this->root = new_root ; } return 0 ; } Funciapush_down()deinserarerecursivnarboreprimeteca parametri nodul curent current n care se ncearc inserarea, i, mpachetat n variabila tuple, valoarea de cheie care se insereaz. Iniial, la primul apel, cmpulnode_ptralvariabileituplevaaveavaloarea0.ncadrul algoritmului de inserare se va cuta valoarea de cheie n nodul curent. Dac aceasta exist deja n nod (arbore), funcia de inserare nu va face nimic i va returna 1. Altfel, variabila key_position va indica: -dac nodul curent este frunz, key_position indic locul n care se va insera valoarea de cheie; -dacnodulcurentnuesteofrunz,key_positionvaindica subarborele n care va trebui s se fac inserarea. Dacnodulcurentestefrunz,inserareaestesimpl:seapeleaz doar metoda push() de inserare a unei chei ntr-un nod (la inserarea ntr-un nodfrunzntotdeaunacmpulnode_ptralparametruluituplevafi0). Dacnodulcurentnuestefrunz,atuncivatrebuiurmritdacnurma inserrii recursive n nodul curent a urcat o valoare de cheie. Daca n nodul curent a urcat o valoare de cheie (metoda push_down() a returnat valoarea 1) atunci: -cmpurile parametrului tuple vor conine valoarea de cheie care a urcat i un pointer ctre subarborele drept corespunztor cheii ; -n nodul curent vor trebui inserate key_value i node_ptr indicate de cmpurile parametrului tuple. nfinal,severificstareancareseaflnodulcurent.Dacstarea acestuia este de OVERFLOW, atunci nseamn ca acesta conine prea multe chei i va trebui divizat. Pentru aceasta se folosete metoda split() a nodului carevaprimicaparametruadresaluituple.nurmaapeluluimetodei split() coninutul cmpurilor key_value i node_ptr ale variabilei tuple vor fi actualizate pentru a indica valoarea de cheie care va urca n nodul printe al noduluicurentipointerulctresubarboreledrept;nacestcazmetoda push_down()vareturna1pentruaindicafaptulcnnodulprintevor trebuifolositecmpurilevariabiletuple.Dacstareanoduluinueste OVERFLOW, metoda push_down() va returna 0. template int btree::push_down( btree_node_tuple* tuple,btree_node_ptrcurrent) { if( 0 == current ) return 0 ; int key_position ; bool duplicate_key = current->find( tuple->key_value, key_position) ; if( duplicate_key ) { /**signal duplicate value*/ return -1 ; } if( current->is_leaf() ) { current->push( tuple->key_value, tuple->node_ptr ); } else { if( push_down( tuple, current->child( key_position ) ) ) current->push( tuple->key_value, tuple->node_ptr); } if( btree_node_type::OVERFLOW == current->status() ) { current->split( tuple ) ; return 1 ; } return 0 ; } 13.4 Algoritmii C++ pentru tergerea unei valori de cheie intr-un arbore B Funciadetergereauneivaloridecheiedintr-unarboreBeste remove().Internestefolositfunciarecursivremove_down().Dacn urmatergeriivaloriidecheienodulrdcinnumaiareniciovaloarede cheie(stareanoduluirdcinesteEMPTY),atuncinouardcina arboreluiestesubarborelestngalvechiirdcini.Vecheardcinse elimin din memorie. template int btree::remove( const key_type& value ) { remove_down( value, this->root ) ; if( btree_node_type::EMPTY == this->root->status() ) { btree_node_ptr old_root = this->root ; this->root = this->root->child( 0 ) ; delete old_root ; } return 0 ; } Funciadetergererecursivauneivaloridecheiedinarboreeste remove_down(). Parametrii primii de funcie sunt valoarea de cheie care se terge i un pointer ctre nodul curent. Dac la un moment dat nodul curent este0,atuncinseamncvaloareadecheiesolicitatafitearsnuse gsetenarbore.Altfel,funciedetipulnoduluiundecheiaestegsit avem urmtoarele cazuri: -dacvaloareadecheiesegsetentr-unnodfrunz,atunci tergereanseamnapelareametodeiremove()anoduluifrunz pentru eliminarea cheii aflat n poziia dat de variabila position ; -dac valoarea de cheie a fost gsit ntr-un nod care nu este nod frunza,atuncivaloareadecheiecaretrebuietearsvafi nlocuit,nimplementareadefa,cuvaloareadecheiecareo precede (se poate demonstra c valoarea de cheie care o precede se gsete ntr-un nod frunz). Dup ce se face nlocuirea folosind funciareplace_with_predecessor(),sevacontinuaalgoritmulde tergere,dardedataaceastasevasolicitatergereavaloriide cheie cu care s-a fcut nlocuirea. Se observ c dac nodul curent este valid (nu este 0) i valoarea de cheie care se caut nu se gsete n nodul curent, atunci variabila position va fi poziionat de metoda find() a nodului ca fiind poziia subarborelui care este posibil s conin valoarea de cheie. nfinalulfunciei,dacnodulcurentnuestefrunz(esteunnod printecarearedescendenicareaufostafectai),setesteazstarea noduluirdcinpentrusubarborelepecares-aefectuatcoborrean procesuldetergere.DacstareaacestuinodesteUNDERFLOW(conine preapuinevaloridecheie),atuncivafiapelatfunciarestore()careva restaura proprietile de arbore B. template int btree::remove_down(const key_type& value,btree_node_ptr current) { if( 0 == current ) { /**signal value not found */ return 0 ; } int position ; if( current->find( value, position ) ) { if( current->is_leaf() ) current->remove_at( position ) ; else { replace_with_predecessor( current, position ) ; remove_down(current->key_value(position ), current->child( position)) ; } } else remove_down( value, current->child( position ) ) ; if( !current->is_leaf() && btree_node_type::UNDERFLOW==current->child(position)->status() ) restore( current, position ) ; return 0 ; } Funcia de nlocuire a unei valori de cheie cu valoarea de cheie care o precedeestereplace_with_succesor().Parametriiprimiidefunciesunt: node,unpointerctrenodulcareconinevaloareadecheiecarese nlocuiete i position care d poziia subarborelui (n nodul indicat de node) careconinevaloareadecheiecareprecedevaloareadecheiecarese nlocuiete. template int btree::replace_with_predecessor(btree_node_ptrnode,int position) { if( position > node->size() ) return 0 ; btree_node_ptr leaf = node->child( position ) ; while( !leaf->is_leaf() ) leaf = leaf->child( leaf->size() ) ; node->node_keys[position] = leaf->key_value( leaf->size() - 1 ) ; return 1 ; } FunciacareasigurrestaurareaproprietilordearboreBeste restore(). Parametrii funciei sunt: un pointer ctre un nod printe (current) iopoziie(position)careindicunsubarborealnoduluicurrent(nodul rdcinalacestuisubarboreconinepreapuinevaloridecheie).Funcia folositnimplementareadefaestecumvaorientatctrestnga,n sensul c mai nti se uit la nodul vecin din stnga pentru a lua o valoare de cheie, folosind nodul vecin din dreapta numai dac nu gsete suficiente valori de cheie n nodul din stnga. Paii care sunt necesari sunt ilustrai n figura 13.22. move_right() z x y wy x zw move_left() y x z wz x y w x y zvw combine() xz v y w Fi gur a 13.22 Paii parcuri n funcia de restaurarea proprietilor unor arbore B template void btree::restore(btree_node_ptrcurrent, const int position) { if( 0 == position ) { if( current->child( 1 )->status() > btree_node_type::MINIMAL ) move_left( current, 1 ) ; else combine( current, 1 ) ; } else if( position == current->size() ) { if(current->child(current->size()-1)->status()> btree_node_type::MINIMAL) move_right( current, position ) ; else combine( current, position ) ; }elseif(current->child(position-1)->status()> btree_node_type::MINIMAL ) move_right( current, position ) ; elseif(current->child(position+1)->status()> btree_node_type::MINIMAL ) move_left( current, position + 1 ) ; else combine( current, position ); } Funciilemove_left(),move_right()icombine()suntuordededus din figura 13.22. template void btree::move_left(btree_node_ptrcurrent,const int position) { btree_node_ptr node = current->child( position - 1 ) ; node->push(current->key_value( position - 1 ),current->child( position )->child( 0 )) ; node = current->child( position ) ; current->node_keys[position - 1] = node->key_value( 0 ); node->node_childs[0] = node->node_childs[1] ; node->shift2left( 0 ) ; node->node_size-- ; } template void btree::move_right(btree_node_ptrcurrent,const int position) { btree_node_ptr node = current->child( position ) ; node->shift2right( 0 ) ; node->node_childs[1] = node->child( 0 ) ; node->node_keys[0] = current->key_value( position - 1 ) ; node->node_size++ ; node = current->child( position - 1 ) ; current->node_keys[position - 1] = node->key_value(node->size()-1); current->child(position)->node_childs[0]=node->child(node->size()); node->node_size-- ; } template void btree::combine(btree_node_ptrcurrent,const int position ) { btree_node_ptr rnode = current->child( position ) ; btree_node_ptr lnode = current->child( position - 1 ) ; lnode->push(current->key_value( position - 1 ), rnode->child( 0 )); for( int idx = 0; idx < rnode->size(); idx++ ) lnode->push( rnode->key_value( idx ), rnode->child( idx + 1 ) ); current->remove_at( position - 1 ) ; delete rnode ; }