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.