drzewo ma dziecko
Transkrypt
drzewo ma dziecko
Algorytmyistrukturydanych Wykład9-Drzewaialgorytmyichprzetwarzania(ciągdalszy) JanuszSzwabiński Planwykładu: Binarnedrzewoposzukiwań(BST) Zrównoważonebinarnedrzewaposzukiwań(AVL) Implementacjatablicyasocjacyjnej(mapy)-podsumowanie Drzewaczerwono-czarne Źródła:większośćilustracjiiprzykładówpochodziz"ProblemSolvingwithAlgorithmsandDataStructuresusingPython", http://interactivepython.org/runestone/static/pythonds/index.html Binarnedrzewoposzukiwań(ang.binarysearchtree,BST) drzewobinarne,wktórym: lewepoddrzewokażdegowęzłazawierawyłącznieelementyokluczachniewiększychniżkluczwęzła prawepoddrzewokażdegowęzłazawierawyłącznieelementyokluczachniemniejszychniżkluczwęzła węzły,opróczklucza,przechowująwskaźnikinaswojegolewegoiprawegosynaoraznaswojegoojca przechodzącdrzewometodąinorderuzyskamyciągkluczyposortowanychniemalejąco kosztyoperacji(wstawianie,wyszukiwanie,usuwanie): proporcjonalnydowysokości(liczbypoziomów)drzewah wprzypadkudrzewzrównoważonychh ≃ log 2n,gdzientoliczbawęzłów ⇒ optymistycznykosztoperacjitoO(logn) wprzypadkudrzewskrajnieniezrównoważonychmożebyćnaweth ≃ n ⇒ pesymistycznykosztwzrastadoO(n) możnawykorzystaćdoimplementacjitablicasocjacyjnych(czyliparklucz-wartość) Interfejsabstrakcyjnejtablicyasocjacyjnej Map()-tworzypustąmapę(tablicęasocjacyjną) put(key,val)-dodajenowąparęklucz-wartośćdotablicy.Jeślikluczjestjużwtablicy,odpowiadającamuwartośćjestzmienianana nową get(key)-odczytujewartośćodpowiadającąkluczowi delmap[key]-usuwaparęklucz-wartośćztablicy len()-liczbaparklucz-wartośćzapisanawtablicy in-operatorprzynależności,zwracaTruejeżelikluczznajdujesięwtablicy Implementacja rozważmynajpierwprocestworzeniadrzewadlanastępującejlistykluczy:70,31,93,94,14,23,73 pierwszyelementlisty,czyli70stajesiękorzeniem 31jestmniejszeod70,więcstajesięjegolewymdzieckiem 93jestwiększeod70,więcstajesięprawymdzieckiemkorzenia 94jestwiększeod70iod93,więcstajesięprawymdzieckiemelementu93 14jestmniejszeod70iod31,więcstajesięlewymdzieckiemelementu31 23jestmniejszeod70i31,alewiększeod14,więcstajesięprawymdzieckiemelementu14 zaimplementujemyBSTprzypomocydwóchklas: TreeNode-węzełdrzewa,zawierareferencjedodzieciirodzicaorazszeregfunkcjipomocniczych,którepozwalają sklasyfikowaćwęzełnapodstawiejegopołożenia BinarySearchTree-właściwedrzewo,zawierareferencjędokorzeniadrzewa,którajestrównaNone(drzewopuste)lub wskazujenakonkretklasyTreeNode Podstawoweklasy In[2]: classTreeNode: def__init__(self,key,val,left=None,right=None,parent=None): self.key=key self.payload=val self.leftChild=left self.rightChild=right self.parent=parent defhasLeftChild(self): returnself.leftChild defhasRightChild(self): returnself.rightChild defisLeftChild(self): returnself.parentandself.parent.leftChild==self defisRightChild(self): returnself.parentandself.parent.rightChild==self defisRoot(self): returnnotself.parent defisLeaf(self): returnnot(self.rightChildorself.leftChild) defhasAnyChildren(self): returnself.rightChildorself.leftChild defhasBothChildren(self): returnself.rightChildandself.leftChild defreplaceNodeData(self,key,value,lc,rc): self.key=key self.payload=value self.leftChild=lc self.rightChild=rc ifself.hasLeftChild(): self.leftChild.parent=self ifself.hasRightChild(): self.rightChild.parent=self szablonwłaściwejklasymożewyglądaćtak: In[1]: classBinarySearchTree: def__init__(self): self.root=None self.size=0 deflength(self): returnself.size def__len__(self): returnself.size def__iter__(self): returnself.root.__iter__() Wstawianieelementówdodrzewa metodaput jeślidrzewojestpuste,tworzymynowąinstancjęTreeNodeiwstawiamyjąwmiejscekorzenia jeślidrzewomajużkorzeń,wówczas: zaczynającodkorzenia,porównujemywartośćnowegokluczazkluczemaktualnegowęzła: jeślijestmniejsza,przechodzimydolewegopoddrzewa jeślijestwiększa,przechodzimydoprawegopoddrzewa jeśliniemapoddrzew,znaleźliśmypozycjędowstawienianowegoklucza tworzymynowąinstancjęTreeNodeiwstawiamyjąnaznalezionąpozycję In[3]: classBinarySearchTree: def__init__(self): self.root=None self.size=0 deflength(self): returnself.size def__len__(self): returnself.size def__iter__(self): returnself.root.__iter__() defput(self,key,val): ifself.root: self._put(key,val,self.root)#_putisahelperfunction else: self.root=TreeNode(key,val) self.size=self.size+1 def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) dodatkowomożemyprzeładowaćjeszczeoperator[],copozwolinakorzystanieznowejstrukturyjakzesłownika: In[4]: classBinarySearchTree: def__init__(self): self.root=None self.size=0 deflength(self): returnself.size def__len__(self): returnself.size def__iter__(self): returnself.root.__iter__() defput(self,key,val): ifself.root: self._put(key,val,self.root)#_putisahelperfunction else: self.root=TreeNode(key,val) self.size=self.size+1 def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) def__setitem__(self,k,v):#overloadingof[]operator self.put(k,v) Odczytywaniewartości metodaget rekursywneprzeszukiwaniedrzewadomomentu: znalezieniapodanegoklucza dojściadoliścia,któregokluczmainnąwartość(kluczaniemawdrzewie) dodatkowoznowuprzeładujemyoperator[],abyzajegopomocąmożnabyłoodczytywaćwartości In[5]: classBinarySearchTree: def__init__(self): self.root=None self.size=0 deflength(self): returnself.size def__len__(self): returnself.size def__iter__(self): returnself.root.__iter__() defput(self,key,val): ifself.root: self._put(key,val,self.root)#_putisahelperfunction else: self.root=TreeNode(key,val) self.size=self.size+1 def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) def__setitem__(self,k,v):#overloadingof[]operator self.put(k,v) defget(self,key): ifself.root: res=self._get(key,self.root) ifres: returnres.payload else: returnNone else: returnNone def_get(self,key,currentNode): ifnotcurrentNode: returnNone elifcurrentNode.key==key: returncurrentNode elifkey<currentNode.key: returnself._get(key,currentNode.leftChild) else: returnself._get(key,currentNode.rightChild) def__getitem__(self,key):#overloadingof[]operator returnself.get(key) Sprawdzanieprzynależności metodagetpozwalananatychmiastowezaimplementowanieoperatorain In[]: classBinarySearchTree: def__init__(self): self.root=None self.size=0 deflength(self): returnself.size def__len__(self): returnself.size def__iter__(self): returnself.root.__iter__() defput(self,key,val): ifself.root: self._put(key,val,self.root)#_putisahelperfunction else: self.root=TreeNode(key,val) self.size=self.size+1 def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) def__setitem__(self,k,v):#overloadingof[]operator self.put(k,v) defget(self,key): ifself.root: res=self._get(key,self.root) ifres: returnres.payload else: returnNone else: returnNone def_get(self,key,currentNode): ifnotcurrentNode: returnNone elifcurrentNode.key==key: returncurrentNode elifkey<currentNode.key: returnself._get(key,currentNode.leftChild) else: returnself._get(key,currentNode.rightChild) def__getitem__(self,key):#overloadingof[]operator returnself.get(key) def__contains__(self,key):#overloadingofinoperator ifself._get(key,self.root): returnTrue else: returnFalse Usuwanieelementów metodadeleteiprzeciążonyoperatordel najtrudniejszazmetod kroki: znalezieniewęzłazpodanymkluczemmetodą_get trzymożliwości-znalezionywęzeł: jestliściem(niemadzieci) najprostszamożliwość usuwamyelementireferencjędoniegowwęźlerodzicu majednodziecko przesuwamydzieckowmiejsceusuwanegorodzica madwojedzieci przeszukujemypoddrzewousuwanegowęzła,abyznaleźćkandydatanajegomiejsce,czylinastępnik(ang. successor) będzietowęzełonajmniejszymkluczuwiększymodkluczawęzłausuwanego powinienonmiećtylkoprawedziecko usuwamygojakwpoprzednimprzypadku wstawiamynamiejsceusuwanegowęzła In[6]: classBinarySearchTree: def__init__(self): self.root=None self.size=0 deflength(self): deflength(self): returnself.size def__len__(self): returnself.size def__iter__(self): returnself.root.__iter__() defput(self,key,val): ifself.root: self._put(key,val,self.root)#_putisahelperfunction else: self.root=TreeNode(key,val) self.size=self.size+1 def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) def__setitem__(self,k,v):#overloadingof[]operator self.put(k,v) defget(self,key): ifself.root: res=self._get(key,self.root) ifres: returnres.payload else: returnNone else: returnNone def_get(self,key,currentNode): ifnotcurrentNode: returnNone elifcurrentNode.key==key: returncurrentNode elifkey<currentNode.key: returnself._get(key,currentNode.leftChild) else: returnself._get(key,currentNode.rightChild) def__getitem__(self,key):#overloadingof[]operator returnself.get(key) def__contains__(self,key):#overloadingofinoperator ifself._get(key,self.root): returnTrue else: returnFalse defdelete(self,key): ifself.size>1: nodeToRemove=self._get(key,self.root) ifnodeToRemove: self.remove(nodeToRemove) self.size=self.size-1 else: raiseKeyError('Error,keynotintree') elifself.size==1andself.root.key==key: self.root=None self.size=self.size-1 else: raiseKeyError('Error,keynotintree') def__delitem__(self,key):#overloadingofdeloperator self.delete(key) defspliceOut(self): defspliceOut(self): ifself.isLeaf(): ifself.isLeftChild(): self.parent.leftChild=None else: self.parent.rightChild=None elifself.hasAnyChildren(): ifself.hasLeftChild(): ifself.isLeftChild(): self.parent.leftChild=self.leftChild else: self.parent.rightChild=self.leftChild self.leftChild.parent=self.parent else: ifself.isLeftChild(): self.parent.leftChild=self.rightChild else: self.parent.rightChild=self.rightChild self.rightChild.parent=self.parent deffindSuccessor(self): succ=None ifself.hasRightChild(): succ=self.rightChild.findMin() else: ifself.parent: ifself.isLeftChild(): succ=self.parent else: self.parent.rightChild=None succ=self.parent.findSuccessor() self.parent.rightChild=self returnsucc deffindMin(self): current=self whilecurrent.hasLeftChild(): current=current.leftChild returncurrent defremove(self,currentNode): ifcurrentNode.isLeaf():#leaf ifcurrentNode==currentNode.parent.leftChild: currentNode.parent.leftChild=None else: currentNode.parent.rightChild=None elifcurrentNode.hasBothChildren():#interior succ=currentNode.findSuccessor() succ.spliceOut() currentNode.key=succ.key currentNode.payload=succ.payload else:#thisnodehasonechild ifcurrentNode.hasLeftChild(): ifcurrentNode.isLeftChild(): currentNode.leftChild.parent=currentNode.parent currentNode.parent.leftChild=currentNode.leftChild elifcurrentNode.isRightChild(): currentNode.leftChild.parent=currentNode.parent currentNode.parent.rightChild=currentNode.leftChild else: currentNode.replaceNodeData(currentNode.leftChild.key, currentNode.leftChild.payload, currentNode.leftChild.leftChild, currentNode.leftChild.rightChild) else: ifcurrentNode.isLeftChild(): currentNode.rightChild.parent=currentNode.parent currentNode.parent.leftChild=currentNode.rightChild elifcurrentNode.isRightChild(): currentNode.rightChild.parent=currentNode.parent currentNode.parent.rightChild=currentNode.rightChild else: currentNode.replaceNodeData(currentNode.rightChild.key, currentNode.rightChild.payload, currentNode.rightChild.leftChild, currentNode.rightChild.rightChild) In[7]: mytree=BinarySearchTree() mytree[3]="red" mytree[4]="blue" mytree[6]="yellow" mytree[2]="at" print(mytree[6]) print(mytree[2]) yellow at Analizabinarnychdrzewwyszukiwania szukającmiejscadowstawienianowejparyklucz-wartość,wnajgorszymwypadkuwykonamyliczbęporównańrównąwysokościdrzewa wysokośćtonicinnegojakliczbakrawędzimiędzykorzenieminajgłębiejpołożonymliściem wydajnośćmetodyputograniczonajestwysokościądrzewa jeśliwartościwstawianesądodrzewawlosowymporządkukluczy,wysokośćh ≃ log2 n ponieważkluczesąlosowouporządkowane,mniejwięcejpołowaznichbędziemniejszaodkorzenia,adrugapołowa-większa wdrzewiebinarnymmamyjedenelementnapoziomiepierwszym(korzeń),maksymalniedwaelementynakolejnymitd. napoziomiedmamyzatem2d elementów jeżelidrzewojestidealniezrównoważone,całkowitaliczbawęzłówwynosin = 2 h + 1 − 1 idealniezrównoważonedrzewomatęsamąliczbęwęzłówwkażdympoddrzewie rzeczywiściemamywięch = log 2n wysokośćjestograniczeniemwydajności-putjestklasyO(logn) jeśliwstawianewartościwstawianesąwedługposortowanychkluczy drzewomawysokośćh = n wtymwypadkuputjestklasyO(n) metodyget,inidelmająpodobneograniczeniawydajności Zrównoważonebinarnedrzewaposzukiwań(AVL) Definicja zrównoważonebinarnedrzewoposzukiwań skrótAVLpochodziodnazwiskrosyjskichmatematyków:Adelsona-VelskiegoorazLandisa(właściwie:GieorgijAdelson-WielskijiJewgienij Łandis) rozwiązujeproblemutrzymaniadobrejwydajnościoperacjinadrzewie(czylijegodobrejstruktury) każdemuwęzłowiprzypisujesięwspółczynnikwyważenia(ang.balancefactor): balanceFactor = height(leftSubTree) − height(rightSubTree) drzewojestzrównoważone,jeśliwspółczynnikwyważeniawynosi0,+1lub-1 wstawiająclubusuwającwęzłytak,abyzachowaćwłasnościdrzewaBST,modyfikujesięrównieżwspółczynnikwyważenia gdywspółczynnikprzyjmujeniedozwolonąwartość,wykonujesięoperacjęrotacjiwęzłówwceluprzywróceniazrównoważenia Wydajność motywacjadladrzewAVLjesttaka,żeutrzymującwspółczynnikzrównoważeniawdopuszczalnychgranicachpoprawimyzłożoność najważniejszychopreacji rozważmy3drzewaowysokościach0,1,2i3wichnajmniejzrównoważonejwersjizachowującejpoprawnewartościwspółczynników wyważenia liczbawęzłówwfunkcjiwysokościwpowyższymprzykładzie: N0 = 1 N1 = 1 + 1 = 2 N2 = 1 + 1 + 2 = 4 N3 = 1 + 2 + 4 = 7 ogólnieotrzymaliśmyzależność N h = 1 + N h −1 + N h −2 zależnośćtabardzoprzypominaciągFibonacciego: F0 = 0 F1 = 1 F i = F i −1 + F i− 2 foralli ≥ 2 wiemy,że lim gdzie\Phitozłotypodział wyraźmyliczbęwęzłówwdrzewieAVLprzezwyrazyciąguFibonacciego:N_h=F_{h+2}-1,h\ge1 ponadtozałóżmy,że:F_i=\Phi^i/\sqrt{5} wówczasN_h=\frac{\Phi^{h+2}}{\sqrt{5}}-1 \begin{eqnarray}\log{N_h+1}&=&(h+2)\log{\Phi}-\frac{1}{2}\log{5}\\h&=&\frac{\log{N_h+1}-2\log{\Phi}+\frac{1}{2}\log{5}}{\log{\Phi}}\\h& =&1.44\log{N_h}\end{eqnarray} drzewaAVLrównieżwnajgorszymprzypadkumająwysokośćrównąpewnejstałejpomnożonejprzezlogarytmzliczbywęzłów wydajnośćpodstawowychoperacjipozostajenapoziomieO(\logN)! Implementacja możemyzaimplementowaćdrzewoAVLjakoklasępochodnądrzewBST musimynadpisaćczęśćdefinicjimetodpomocniczychnatakie,którezachowująwłasnościdrzewAVL Wstawianieelementów nowekluczesąwstawianedodrzewajakoliście ichwspółczynnikwyważeniawynosi0 musimyjednakzaktualizowaćwspółczynnikrodzica następnierekursywniewyliczamynanowowspółczynnikiwszystkichprzodków dwaprzypadkibazowe: doszliśmydokorzenia rodzicpoaktualizacjimawspółczynnikwyważeniarówny0(jeślijakieśpoddrzewomawspółczynnik0,towspółczynnikprzodka nieulegazmianie musimynadpisaćmetodę_put idopisaćnowąfunkcjępomocnicządoaktualizowaniawspółczynnikawyważenia In[8]: classAVLTree(BinarySearchTree): def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) self.updateBalance(currentNode.leftChild) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) self.updateBalance(currentNode.rightChild) defupdateBalance(self,node): ifnode.balanceFactor>1ornode.balanceFactor<-1: self.rebalance(node) return ifnode.parent!=None: ifnode.isLeftChild(): node.parent.balanceFactor+=1 elifnode.isRightChild(): node.parent.balanceFactor-=1 ifnode.parent.balanceFactor!=0: self.updateBalance(node.parent) abyponowniezrównoważyćdrzewo,koniecznamożebyćrotacjawęzłów rotacjawlewoodbywasięwnastępującysposób: prawedzieckoBstajesiękorzeniempoddrzewa dawnykorzeńjestteraznowymlewymdzieckiem jeśliwęzełBmiałprawedziecko,pozostawje wkolejnymprzykładziewymaganajestrotacjawprawo przesuńCwmiejscekorzenia poprzednikorzeńEstajesięprawymdzieckiemnowegokorzenia jeśliCmiałprawedziecko(D),stajesięonolewymdzieckiemnowegoprawegodziecka(E) In[9]: classAVLTree(BinarySearchTree): def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) self.updateBalance(currentNode.leftChild) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) self.updateBalance(currentNode.rightChild) defupdateBalance(self,node): ifnode.balanceFactor>1ornode.balanceFactor<-1: self.rebalance(node)#tobeimplemented return ifnode.parent!=None: ifnode.isLeftChild(): node.parent.balanceFactor+=1 elifnode.isRightChild(): node.parent.balanceFactor-=1 ifnode.parent.balanceFactor!=0: self.updateBalance(node.parent) defrotateLeft(self,rotRoot): newRoot=rotRoot.rightChild#keeptrackofthenewroot rotRoot.rightChild=newRoot.leftChild#rightchildoftheoldrootreplacedwiththeleftchild ofthenew ifnewRoot.leftChild!=None: newRoot.leftChild.parent=rotRoot newRoot.parent=rotRoot.parent ifrotRoot.isRoot(): self.root=newRoot else: ifrotRoot.isLeftChild():#iftheoldrootisaleftchildthenwechangethepa rent rotRoot.parent.leftChild=newRoot#oftheleftchildtopointtothenewroot;otherwise we else:#changetheparentoftherightchildtopoint rotRoot.parent.rightChild=newRoot#tothenewroot newRoot.leftChild=rotRoot rotRoot.parent=newRoot rotRoot.balanceFactor=rotRoot.balanceFactor+1-min(newRoot.balanceFactor,0)#aktualizacjawspó łczynnika newRoot.balanceFactor=newRoot.balanceFactor+1+max(rotRoot.balanceFactor,0) wostatnichdwóchliniachmetodyrotateLeftzmieniliśmywspółczynnikizrównoważenianowegoipoprzedniegokorzenia ponieważpozostałeruchyprzesuwającałepoddrzewa,pozostałewspółczynnikinieulegajązmianie rozważmyrotacjęwlewojaknapowyższymrysunku BiDtorotowaneelementy,resztatoichpoddrzewa niechh_xopisujewysokośćpoddrzewaokorzeniuwwęźlex zdefinicjimamy\begin{eqnarray}newBal(B)&=&h_A-h_C\\oldBal(B)&=&h_A-h_D\end{eqnarray} wysokośćwęzłatodłuższazwysokościjegopoddrzewzwiększonao1:h_D=1+\max(h_C,h_E) oldBal(B)=h_A-(1+\max(h_C,h_E)) odejmującrównaniananowyistarywspółczynnikdlawęzłaBotrzymamy \begin{split}newBal(B)-oldBal(B)=h_A-h_C-(h_A-(1+\max(h_C,h_E)))\\newBal(B)-oldBal(B)=h_A-h_C-h_A+(1+\max(h_C,h_E))\\ newBal(B)-oldBal(B)=h_A-h_A+1+\max(h_C,h_E)-h_C\\newBal(B)-oldBal(B)=1+\max(h_C,h_E)-h_C\end{split} korzystamyzwłasności \max(a,b)-c=\max(a-c,b-c) ostatecznieotrzymamy\begin{split}newBal(B)=oldBal(B)+1+\max(0,-oldBal(D))\\newBal(B)=oldBal(B)+1-\min(0,oldBal(D)) \\\end{split} innymisłowyniemusimywyliczaćwysokościpoddrzew,żebyzaktualizowaćwspółczynnikiwyważeniawęzłaB podobnywywódmożnaprzeprowadzićdlawęzłaD zrotacjązwiązanyjestjeszczejedenproblem: współczynnikwęzławynosi-2,więcpowinniśmyrotowaćwlewo wtedyotrzymamyjednak potrzebujemydodatkowychwarunków: jeślipoddrzewowymagarotacjiwlewo(współczynnikkorzeniamniejszyod0),sprawdźwspółczynnikprawegodziecka: jeślidzieckomawspółczynnikwiększyodzera(dłuższalewagałąź),rotujwprawowzględemprawegodziecka, anastępniewlewowzględemkorzenia jeślipoddrzewowymagarotacjiwprawo(współczynnikkorzeniawiększyod0),sprawdźwspółczynniklewegodziecka: jeślidzieckomawspółczynnikmniejszyodzera(dłuższaprawagałąź),rotujwlewowzględemlewegodziecka, anastępniewprawowzględemkorzenia In[10]: classAVLTree(BinarySearchTree): def_put(self,key,val,currentNode): ifkey<currentNode.key: ifcurrentNode.hasLeftChild(): self._put(key,val,currentNode.leftChild) else: currentNode.leftChild=TreeNode(key,val,parent=currentNode) self.updateBalance(currentNode.leftChild) else: ifcurrentNode.hasRightChild(): self._put(key,val,currentNode.rightChild) else: currentNode.rightChild=TreeNode(key,val,parent=currentNode) self.updateBalance(currentNode.rightChild) defupdateBalance(self,node): ifnode.balanceFactor>1ornode.balanceFactor<-1: self.rebalance(node) return ifnode.parent!=None: ifnode.isLeftChild(): node.parent.balanceFactor+=1 elifnode.isRightChild(): node.parent.balanceFactor-=1 ifnode.parent.balanceFactor!=0: self.updateBalance(node.parent) defrotateLeft(self,rotRoot): newRoot=rotRoot.rightChild#keeptrackofthenewroot rotRoot.rightChild=newRoot.leftChild#rightchildoftheoldrootreplacedwiththeleftchild ofthenew ifnewRoot.leftChild!=None: newRoot.leftChild.parent=rotRoot newRoot.parent=rotRoot.parent ifrotRoot.isRoot(): self.root=newRoot else: ifrotRoot.isLeftChild():#iftheoldrootisaleftchildthenwechangethepa rent rotRoot.parent.leftChild=newRoot#oftheleftchildtopointtothenewroot;otherwise we else:#changetheparentoftherightchildtopoint rotRoot.parent.rightChild=newRoot#tothenewroot newRoot.leftChild=rotRoot rotRoot.parent=newRoot rotRoot.balanceFactor=rotRoot.balanceFactor+1-min(newRoot.balanceFactor,0)#aktualizacjawspó łczynnika newRoot.balanceFactor=newRoot.balanceFactor+1+max(rotRoot.balanceFactor,0) defrebalance(self,node): ifnode.balanceFactor<0: ifnode.rightChild.balanceFactor>0: self.rotateRight(node.rightChild) self.rotateLeft(node) else: self.rotateLeft(node) elifnode.balanceFactor>0: ifnode.leftChild.balanceFactor<0: self.rotateLeft(node.leftChild) self.rotateRight(node) else: self.rotateRight(node) powstawieniunowegoelementujakoliścia aktualizacjawspółczynnikówwszystkichprzodkówtoconajwyżejlog_2(n)operacji(jednanakażdympoziomie) conajwyżejdwierotacje,abyprzywrócićzrównoważenie rotacjesąklasyO(1) \RightarrowputpozostajeklasyO(\log_2n) metodygetiinsątakiesame,jakdladrzewBST metodadelrównieżbędziewymagałarotacji,jednakpodobniejakwprzypadkuputjejzłożonośćczasowaniezmienisięwstosunkudo drzewBST Implementacjatablicyasocjacyjnej(mapy)-podsumowanie Operacja Listaposortowana Tablicahashująca Drzewabinarne DrzewaAVL put O(n) O(1) O(n) O(log_2n) get O(log_2n) O(1) O(n) O(log_2n) in O(log_2n) O(1) O(n) O(log_2n) del O(n) O(1) O(n) O(log_2n) Drzewaczerwono-czarne samoorganizującesiędrzewobinarneposzukiwań wynalezioneprzezRudolfaBayeraw1972r(jakosymetrycznebinarneB-drzewa) używanenajczęściejdoimplementacjitablicasocjacyjnych skomplikowanewimplementacji niskazłożonośćobliczeniowaelementarnychoperacji zkażdymwęzłempowiązanyjestdodatkowyatrybut-kolor,którymożebyćczerwonylubczarny oprócztypowychwłasnościdrzewBSTwprowadzonokolejnewymagania: 1. każdywęzełjestczerwonyalboczarny 2. korzeńjestczarny 3. każdyliśćjestczarny(możnatraktowaćniljakoliść) 4. jeśliwęzełjestczerwony,tojegosynowiemusząbyćczarni 5. każdaścieżkazustalonegowęzładoliścialiczytylesamoczarnychwęzłów wymaganiategwarantują,żenajdłuższaścieżkaodkorzeniadoliściabędzieconajwyżejdwukrotniedłuższaniżnajkrótsza: 1. Zgodniezwłasnością4,żadnaścieżkaniezawieradwóchczerwonychwęzłówzrzędu,jednakmożezawieraćczarne.Stąd najkrótszaścieżkaodwęzłaXzawierawyłącznienczarnychwęzłów. 2. Zgodniezwłasnością5,drugaścieżkawychodzącazwęzłaXmusizawieraćtakżenczarnychwęzłów.Jedynymsposobem,aby miałaonainnąłącznądługość,jestumieszczeniepomiędzykażdąparąwęzłówczarnychwęzłaczerwonego. 3. Zgodniezwłasnością3,liśćkończącyobieścieżkimusibyćczarny.JeżeliwęzełXjestczarny,wtedywścieżcemożemy rozmieścićconajwyżejn-1węzłówczerwonych,wprzeciwnymzaśraziebędziemymieliwniejnczerwonychwęzłów(wliczającw tosamX). dlanwęzłówgłębokośćdrzewaczerwono-czarnegohwyniesienajwyżej2\log(n+1),przezcoelementarneoperacjebędąwykonywaćsię wczasieO(\logn) podobnie,jakdrzewaAVLwymagająrotacjiwęzłówcelemprzywróceniawłasności pesymistycznazłożonoścczasowajesttakasamajakdrzewAVL,jednakdrzewaAVLsąbardziejwydajneprzypowtarzającychsię wyszukiwaniach(http://web.stanford.edu/~blp/papers/libavl.pdf)