Binära träd (forts)
●
Ett binärt träd kan lagras i ett enda
sammanhängande minne
●
Roten har index 1
●
Vänster barn till nod i har index 2*i
●
Höger barn till nod i har index 2*i + 1
●
Föräldern till nod i har index = heltalsdelen av i/2
Linjärt minne
●
●
Sammanhängande minne är ej flexibelt
–
Om trädet inte är fullt reserveras onödigt mycket
minne
–
Insättning av nya noder kräver kopiering
Effektivt
–
Traversering
–
Vet man storleken innan och den inte ändras så
elimineras problemen
–
Återkommer i samband med sortering
Höjden på ett fullt träd
Höjd=1, n=1
Höjd=2, n=3
Höjd=3, n=7
Höjd=4, n=15
Höjden på ett fullt träd
Höjd=1, n=1, 2h=2
Höjd=2, n=3, 2h=4
Höjd=3, n=7, 2h=8
Höjd=4, n=15, 2h=16
Så att n = 2h-1
Eller h = lg(n+1)
●
Att ta sig från roten till ett löv är väsentligt
effektivare än att ta sig igenom alla element
N
16
1024
1048576
4294967296
●
lg N
4
10
20
32
Vi ska senare se hur detta kan utnyttjas
Tillämpning av Binära träd
*
4
+
+
+
1
4
+
3
2
*
1
3
2
Binära träd kan användas för att på ett intuitivt sätt representera uttryck.
Det vänstra trädet representerar 4 * (1 + 2 + 3)
Och det högra 4 + 1 * 2 + 3
Hur traversera för att beräkna ett träds värde (pre-, in- eller postorder)?
Skriv funktionen!
Postorder
●
●
Utskrift av ett binärt uttrycksträd enligt
postorder ger ett postfix uttryck
Utskrift enligt inorder ger ett infix (”vanligt”)
uttryck
Övning
-
/
1
-
-
2
+
4
3
1
+
*
2
3
5
4
Rita trädet för a + b + c på två sätt (olika + är rot). Är de likvärdiga?
Rita träden för uttrycken 1 / (2 + 3 * 4) och 1 / 2 + 3 * 4
Vilka infix uttryck representerar respektive träd ovan?
Postfix?
Trädkonstruktion
●
Algoritm för att bygga ett träd från postfix uttryck
(snarlikt det du gjorde i lab 2)
●
Input: Ett postfix uttryck och en tom stack
●
Output: En stack med roten till ett binärt träd
●
Upprepa tills input är tomt:
–
Läs nästa symbol, X, från input
–
Om X är en operand
●
–
Skapa en trädnod av X och pusha noden
Annars (X är en operator)
●
●
●
Poppa två träd, T2 och T1, från stacken
Skapa en trädnod, n, med X som data, T1 som
vänsterbarn och T2 som högerbarn
Pusha n på stacken
Träd och postfix
●
●
●
Trädet kan på så sätt användas för att skapa
ett infix uttryck som motsvarar ett postfix
uttryck.
Ett problem man oftare vill lösa är att givet ett
infix uttryck (skrivet av människor) skapa ett
binärt träd (för ”utskrift” enligt postorder)
Då behöver vi en algoritm för att bygga träd
från infix uttryck
Skapa träd från infix uttryck
●
Här är en iterativ algoritm (för enbart +, * och
tal):
●
Skapa en nod, n1, av första termen
●
Upprepa följande tills slut på input
●
–
Läs nästa symbol från input (det är ett +)
–
Skapa en ny nod, n2, av nästa term
–
Skapa en ny nod, n, av + och med n1 och n2 som
vänster resp. höger barnnod
–
Sätt n1 = n
Returnera n1
Skapa träd från infix uttryck
●
Term kan göras på liknande sätt:
●
Skapa en nod, n1, av första talet
●
Upprepa följande tills slut på term
●
–
Läs nästa symbol från input (det är ett *)
–
Skapa en ny nod, n2, av nästa tal
–
Skapa en ny nod, n, av * och med n1 och n2 som
vänster resp. höger barnnod
–
Sätt n1 = n
Returnera n1
Skapa träd från infix uttryck
●
●
●
Genom att man särskiljer på behandlingen av
den högprioriterade operationen * och den
lågprioriterade +, på så sätt att ”* delträd”
byggs under + så kommer de att ges rätt
prioritet då trädet senare beräknas.
Operatorer av fler prioriteter kan läggas till på
liknande sätt.
Parenteser kan också behandlas på ett
liknande sätt men man får en rekursion tillbaks
till första nivån (lägst prioritet) då man ”kommit
in i” ett parentessatt uttryck.
Skapa träd från infix uttryck
Här är en rekursiv algoritm för att lösa samma
sak:
●
uttryck-till-träd(input, rot)
–
om rot = tomtträd så
●
–
om ej tomt(input) så
●
●
●
–
rot := term(input, tomtträd)
Läs nästa symbol från input (det är ett +)
rot := skapaTräd(rot, +, term(input, tomtträd))
rot := uttryck-till-träd(input, rot)
return rot
Skapa träd från infix uttryck
Funktionen term kan definieras på liknande sätt:
●
term(input, rot)
–
om rot = tomtträd så
●
●
–
om nästa(input) är ”*” så
●
●
●
●
–
Läs nästa symbol, X, från input (det är ett tal)
rot := skapaTräd(X)
Läs nästa symbol från input (det är ett *)
Läs nästa symbol, X, från input (det är ett tal)
rot := skapaTräd(rot, *, skapaTräd(X))
rot := term(input, rot)
return rot
Implementation i C:
●
●
Vi låter nodernas data innehålla strängar som
visar vilken operand eller operator det
representerar.
Vi förenklar problemet på följande sätt.
–
Anta att indata utgörs av en stack där elementen är
strängar (operander eller operatorer) på infix form.
–
BTree har även följande operation:
/* Funktionen returnerar roten till ett nytt träd som har left och right som
sitt vänstra resp. högra barnträd, och data som datainnehåll. */
Btree *BtreeMerge(Btree *left, Btree *right, void *data);
Implementation i C:
BTree *expr(Stack *input, BTree *root) {
BTree *n1, *n2;
if (root == NULL)
root = term(input, NULL);
if (!StackIsEmpty(input)) {
StackPop(input);
n1 = root;
n2 = term(input, NULL);
root = expr(input, BtreeMerge(n1, n2, ”+”));
}
return root;
}
Implementation i C:
BTree *term(Stack *input, BTree *root) {
BTree *n1, *n2;
if (root == NULL)
root = BTreeCreate(StackPop(input));
if (strcmp(StackPeek(input), ”*”) == 0) {
StackPop(input);
n1 = root;
n2 = BTreeCreate(StackPop(input));
root = term(input, BtreeMerge(n1, n2, ”*”));
}
return root;
}
Implementation i C:
●
●
Även här kan vi bara ”följa mönstret” och
utöka med nya funktioner för att klara att
hantera uttryck med fler prioritetsnivåer.
Flera operatorer med samma prioritet kan
enkelt behandlas i samma funktion.