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_2⁡n)
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)

Podobne dokumenty