Med två referenser i varje objekt kan man emellertid bygga
mer intressanta träd, till exempel
ett som beskriver en symfoniorkesters
sammansättning.
Här har objekten följande utseende.
class Node: value="" down=None right=NoneAll systematisk uppdelning kan beskrivas med liknande träd, till exempel ett bokverks uppdelning i delar, kapitel, sektioner osv. Man kan också tänka sej det som ett släktträd och då kallas ofta
down
-referensen för
firstChild
och rightreferensen för nextSibling
.
Det är ganska förvånande att det räcker med två referenser i varje
objekt, oavsett hur stora barnaskarorna är.
När man talar om binärträd brukar man tänka
på en lite annorlunda bild.
Högst upp finns konstigt nog trädets rot och dit
har man alltid en referens
root
.
Antalet nivåer i trädet avgör hur många objekt det kan innehålla.
Ett fullt träd med k nivåer innehåller 2 höjt till k
objekt minus 1. Exempel: k=3 i vår bild ger högst 7 objekt (det finns plats
för två till under 9999). Man kan också säga att ett balanserat träd
med N objekt har cirka log N nivåer.
write(root)
skriver ut
1 17 666 4711 9999 för vårt träd.
//Inordning def write(p): if p is None: return write(p.left) print p.value write(p.right)Om man kastar om dom tre sista satserna får man ändå ut alla talen på skärmen men i andra ordningar. Preordning (eng. preorder) innebär att rottalet skrivs först, sedan vänsterträdet och sist högerträdet. I vårt exempel blir ordningen 4711 17 1 666 9999.
Om vi återgår till orkesterträdet kan vi se att preordning faktiskt ger vettigare utskrift. Så här blir koden i det fallet.
//Preordning def write(p): if p is None: return print p.value write(p.down) write(p.right)Utskriften blir då den naturliga. Om vi för tydlighets skull använder indragning av orden på lägre nivå blir utskriften
Orkester Blås Trä Bleck Stråk Vi Va Vc Kb Slag(Är det någon som kommer på hur man måste ändra koden för att få just dessa indragningar, så vill jag gärna höra förslaget.)
Slutligen kan man skriva ut i postordning (eng. postorder) och det innebär att vänsterträdet skrivs först, sedan högerträdet och sist roten. Det ger 1 666 17 9999 4711 i vårt exempel.
finns(p,value)
returnera True
ifall ordet
finns i det delträd där p
är rot.
def finns(p,value): if p is None: return False if value==p.value: return True if value<p.value: return finns(p.left,value) if value>p.value: return finns(p.right,value)Men den kan också göras utan rekursion. Komplexiteten blir densamma.
def finns(p,value): while True: if p is None: return False if value==p.value: return True if value<p.value: p=p.left if value>p.value: p=p.rightDet här är ju nästan precis samma sak som binär sökning i en vektor. I båda fallen blir antalet jämförelser cirka log N, men vektorn har två nackdelar.
bintree.py
och vi vill använda det som en abstrakt ordlista.
För en abstrakt ordlista bör åtminstone tre anrop finnas:
exists
, put
och write
.
from bintree import Bintree wordlist=Bintree() # Skapar ett trädobjekt - - - if wordlist.exists(word):...# Returnerar True om ordet finns i trädet wordlist.put(word) # Sorterar in ett nytt ord i trädet wordlist.write() # Skriver ut alla ord i bokstavsordningEndast dessa tre metoder kan anropas utifrån. Men om man vill implementera metoderna rekursivt måste man kunna göra anrop av typen
finns(p,value)
och därför definierar man ytterligare tre
metoder i filen bintree.py
, men ovanför klassen Bintree
.
Den metod som anropas wordlist.exists(word)
innehåller bara en sats.
def exists(self,value): return finns(self.root,value)Den låter alltså
finns
göra jobbet. Varför kan då inte
huvudprogrammet direkt göra anropet if finns(wordlist.root,word)
?
Det skulle ju strida mot abstraktionen - det är inte meningen att någon
utomstående ens ska känna till att det finns en root
!
Svårast att implementera är insättningen. Man skulle tro att satsen
putta(self.root,newvalue)
skulle kunna skicka vidare jobbet till en rekursiv metod putta
,
men det går inte. När trädet är tomt är rotpekaren None
, men när
den första noden sätts in ska rotpekaren peka på den. Men self.root
kan bara ändras inifrån objektet, så därför får man skriva så här.
def put(self,newvalue): self.root=putta(self.root,newvalue)Anropet till putta returnerar en pekare till det nya trädet och den läggs in i
self.root
. Men det är bara första gången som tilldelningen
behövs. Så här blir principen för putta.
def putta(p,newvalue): if p is None: # Skapa en ny nod med det nya - - - # värdet och returnera den if newvalue<p.value: p.left=putta(p.left,newvalue) # Rekursiv inputtning elif newvalue>p.value: p.right=putta(p.right,newvalue) else: # Värdet fanns redan i trädet, - - - # gör felutskrift! return p # Returnera pekare till det # modifierade trädet
class Node: key="" # Det som man sorterar på info=None # Informationen om key left=None right=NoneSökmetoden
getInfo(key)
blir likadan som exists
men returnerar p.info
i stället för True/False
.
Adjunkt är en ren undervisningstjänst - en lektor ska forska i tjänsten och undervisar kanske åttio procent - en professor leder en forskargrupp och hinner sällan undervisa mer än femtio procent.