Rekursiv kommer från latinet och betyder återlöpande. Om man i definitionen av ett begrepp använder begreppet självt så är definitionen rekursiv. Rekursiva tankar kan också användas för problemlösning.
def s(n): if n == 1: return 1 else: return s(n-1) + n
s(0) |
s(1) |
s(2) |
s(3) |
s(4) |
huvudprogram |
Aktiveringsposterna tar extra minne i anspråk och stackhanteringen tar extra tid, så den extremt sparsamme skriver om sina rekursiva funktioner.
top
pekar på den
översta noden.def antal(p): if p is None: return 0 else: return 1 + antal(p.next)Anropet
antal(top)
ger nu rätt svar!
def binarySearch(alist, item): if len(alist) == 0: return False else: midpoint = len(alist)/2 if alist[midpoint] == item: return True else: if item < alist[midpoint]: return binarySearch(alist[:midpoint], item) else: return binarySearch(alist[midpoint+1:], item)
class Node: def __init__(self, value): self.value = value self.down = None self.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äcker med två referenser i varje objekt, oavsett hur stora barnaskarorna är.
class Node: def __init__(self, value): self.value = value self.left = None self.right = NoneMan når trädet genom variabeln
root
som pekar på
den översta noden (datalogiska träd har roten uppåt). Rotnodens
vänsterpekare pekar på ett mindre binärträd och högerpekaren på
ett annat mindre binärträd. Och det konstaterandet kan ses som en
rekursiv definition av begreppet binärträd!
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.
def antal(p): if p == None: return 0 else: return 1 + antal(p.left) + antal(p.right)Anropet
antal(root)
ger nu rätt svar!
Om man ska skriva ut alla talen i trädet vill man oftast göra det i
så kallad inordning (eng. inorder), dvs från vänster
till höger.
Fråga: Hur skriver man ut trädet i inordning?
Rekursivt svar: Först skriver man ut vänsterträdet,
sedan rottalet, sist högerträdet.
...men ett tomt träd skriver man inte alls.
Följande funktion gör att write(root)
skriver ut
1 17 666 4711 9999 för vårt träd.
#Inordning def write(p): if p != None: 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 != None: 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(Hur gör man för att få dessa indragningar?)
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.
Algoritmer som går igenom varje nod i trädet (t ex utskrift) har tidskomplexitet O(n).
finns(p,value)
returnera True
ifall ordet
finns i det delträd där p
är rot.
def finns(p,value): if p == 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)Om trädet är balanserat tar sökningen O(logn), därför att vi som mest går igenom trädets höjd. Den kan också göras utan rekursion, tidskomplexiteten blir densamma. Det här är ju nästan precis samma sak som binärsökning i en indexerad datastruktur (som
list
i Python).