Föreläsning 8 Datastrukturer i kursen Innehåll Hittills har vi använt datastrukturerna vektor och enkellänkad lista. Träd, speciellt binära träd T.ex. för att implementera de abstrakta datatyperna lista, stack och kö. egenskaper användningsområden implementering I kursen används också träd binära sökträd och hashtabeller Undervisningsmoment: föreläsning 8, övningsuppgifter 8, lab 4 för att implementera de abstrakta datatyperna mängd (eng. Set) och lexikon (eng. Map). Avsnitt i läroboken: heapar 6.1 – 6.3 för att implementera den abstrakta datatypen prioritetskö. I 2:a upplagan: 6.1 – 6.3 PFK (Föreläsning 8) VT 2017 1 / 32 Träd PFK (Föreläsning 8) VT 2017 2 / 32 Träd – rekursiv definition Icke-linjär struktur Ett träd är antingen En nod i ett träd kan ha högst en föregångare (förälder), men flera efterföljare (barn). tomt (har inga noder) eller består av en speciell nod, roten, och noll eller flera subträd. rot tre subträd Jfr. listor som är linjära strukturer En nod i en lista kan ha högst en föregångare och en efterföljare. PFK (Föreläsning 8) VT 2017 3 / 32 PFK (Föreläsning 8) VT 2017 4 / 32 Släkträd Terminologi lånad från släktträd Exempel på användning av träd Antag att två noder n1 och n2 är förbundna med en båge och n1 är den övre av dem Karl XIV Johan 1763-1844 n1 är då förälder till n2 och n2 är barn till n1 Oskar I 1799-1876 Karl XV 1826-1872 Gustaf 1827-1852 Noder som har samma förälder kallas syskon (eng: siblings) Oskar II 1829-1907 Eugenie 1830-1889 PFK (Föreläsning 8) Antag att två noder n1 och n2 är förbundna med en serie bågar och n1 är den övre av dem August 1831-1873 VT 2017 n1 är då förfader (eng: ancestor ) till n2 n2 är avkomling (eng: descendant) till n1 5 / 32 Terminologi lånad från naturen PFK (Föreläsning 8) VT 2017 6 / 32 Begreppen nivå eller djup för en nod Rot – den enda nod som saknar förälder I datavetenskap ritas träden ”upp och ned” med roten överst. Rotens nivå (djup) är 1. Löv – noder som saknar barn En nod som inte är rot har nivå (djup) = förälderns nivå (djup) + 1. Gren – en serie noder förbundna med varandra nivå = djup = 1 rot nivå = djup = 2 löv En gren i trädet visas med: löv PFK (Föreläsning 8) löv nivå = djup = 3 löv VT 2017 7 / 32 PFK (Föreläsning 8) VT 2017 8 / 32 Begreppet höjd för ett träd Binära träd Ett tomt träd har höjden 0. Ett träd är binärt om varje nod har högst två barn (eller ekvivalent, två subträd). Ett träd som inte är tomt har höjd = maximala nivån (djupet) för noderna i trädet. Ett träd är strikt binärt om varje nod har noll eller två barn. D.v.s. alla noder som inte är löv har två barn. trädets höjd = 3 nivå = djup = 3 PFK (Föreläsning 8) Ej binärt, roten har tre barn VT 2017 9 / 32 Binära träd – speciell terminologi Binärt, men ej strikt binärt Strikt binärt PFK (Föreläsning 8) VT 2017 10 / 32 Huffmanträd Exempel på användning av (strikt) binära träd För ett binärt träd brukar man tala om vänster respektive höger subträd till en nod vänster respektive höger barn till en nod I Huffmankodning byts tecken ut mot koder av olika längd. Vanligt förekommande tecken ska ha kortare kod än mer sällsynta tecken. Vänster barn till roten Höger barn till roten Vänster subträd till roten Höger subträd till roten Tecken a b c d Kod 0 100 101 11 0 1 a 0 1 d 0 b 1 c OBS: en nod kan ha höger barn utan att ha vänster barn. PFK (Föreläsning 8) PFK (Föreläsning 8) VT 2017 11 / 32 VT 2017 12 / 32 Aritmetiska uttryck Binära sökträd Exempel på användning av (strikt) binära träd Exempel på användning av binära träd Ett aritmetiskt uttryck med (de binära) operationerna +, –, * och / kan representeras av ett (strikt) binärt träd: Mona * + Hans * 1 + 3 Ann 2 3 Nora 1 Karl 1+2*3 (1 + 2) * 3 Operander lagras i löv, operatorer i övriga noder. Ett träds värde = operatorn i roten applicerad på värdet av subträden. Ett lövs värde är det värde som lagrats i lövet. PFK (Föreläsning 8) VT 2017 13 / 32 Lagrar element med söknyckel för vilken jämförelseoperationer är definierade. Här: sträng. Nycklarna i vänster subträd är mindre än rotens nyckel som i sin tur är mindre än nycklarna i höger subträd. Fördel: Enkelt och snabbt att leta upp, sätta in och ta bort element. PFK (Föreläsning 8) Implementering av binära träd Implementering av binära träd Klassen BinaryTree Klassen Node public class BinaryTree<E> { private Node<E> root; public BinaryTree() { root = null; } root null private Node(E data) { this.data = data; left = right = null; } private static class Node<E> { // se nästa bild } PFK (Föreläsning 8) VT 2017 14 / 32 VT 2017 16 / 32 private static class Node<E> { private E data; private Node<E> left; private Node<E> right; ... operationer på trädet ... } Tora 2 } VT 2017 15 / 32 data left right ... PFK (Föreläsning 8) Skriv ut trädets noder Rekursiva traverseringar av binära träd public void print() { print(root); } Traversering betyder att vi besöker varje nod i trädet. Många operationer på träd kan tolkas som traversering. private void print(Node<E> n) { if (n != null) { System.out.println(n.data); print(n.left); print(n.right); } } Under traverseringen utför man operationer på noderna. Man kan traversera träd i olika ordningar, t ex följande rekursivt definierade: H S A I vilken ordning skrivs noderna ut? preorder: först roten, sedan vänster subträd i preorder, därefter höger subträd i preorder inorder: först vänster subträd i inorder, sedan roten och därefter höger subträd i inorder postorder: först vänster subträd i postorder, sedan höger subträd i postorder och därefter roten M K N PFK (Föreläsning 8) T VT 2017 17 / 32 Diskutera VT 2017 18 / 32 Algoritm för preordertraversering if trädet tomt return else besök roten traversera vänster subträd traversera höger subträd M H A S K N T Exempel: Skriv ut innehållet i alla noder i preorder i det träd där n är rot. private void print(Node<E> n) { if (n != null) { System.out.println(n.data); print(n.left); print(n.right); } } I vilken ordning behandlas noderna om man traverserar trädet i preorder? inorder? postorder? PFK (Föreläsning 8) PFK (Föreläsning 8) VT 2017 19 / 32 PFK (Föreläsning 8) VT 2017 20 / 32 Algoritm för inorder- och postordertraversering Traversera trädet rekursivt – alternativ Publik metod i trädklassen Inorder: if trädet tomt return else traversera vänster subträd besök roten traversera höger subträd Alternativt kan vi placera den rekursiva metoden i nodklassen. I klassen BinaryTree implementerar vi då endast följande publika metod: /** Skriver ut trädet i preorder */ public void print() { if (root != null) { root.print(); } } Postorder: if trädet tomt return else traversera vänster subträd traversera höger subträd besök roten PFK (Föreläsning 8) Metoden print i klassen Node<E> finns på nästa bild. VT 2017 21 / 32 Traversera trädet rekursivt – alternativ PFK (Föreläsning 8) VT 2017 22 / 32 Exempel: visualisera träd Rekursiv metod i nodklassen Visualisera innehåll och form på ett träd genom att översätta trädet till en sträng enligt: a b PFK (Föreläsning 8) VT 2017 null d null null a /* Skriver ut det träd där noden är rot i preorder. */ private void print() { System.out.println(data); if (left != null) { left.print(); } if (right != null) { right.print(); } } b c c d e e null null null Strängen beskriver trädet i preorder Barn indenteras 2 blanksteg i förhållande till föräldern Varje nod på ny rad Tomma subträd representeras av delsträngen ”null” 23 / 32 PFK (Föreläsning 8) VT 2017 24 / 32 Visualisera träd – implementering Visualisera träd – implementering Enklare problem – sträng med trädets innehåll public String toString() { StringBuilder sb = new StringBuilder(); buildString(root, 0, sb); return sb.toString(); } public String toString() { StringBuilder sb = new StringBuilder(); buildString(root, sb); return sb.toString(); } private void buildString(Node<E> n, StringBuilder sb) { if (n != null) { sb.append(n.data); sb.append(’\n’); buildString(n.left, sb); buildString(n.right, sb); } } Observera att man skapar ett enda StringBuilder-objekt. Det måste skickas med som parameter till den rekursiva metoden. PFK (Föreläsning 8) VT 2017 25 / 32 private void buildString(Node<E> n, int indent, StringBuilder sb) { for (int i = 0; i < indent; i++) { sb.append(’ ’); } if (n == null) { sb.append("null\n"); } else { sb.append(n.data); sb.append(’\n’); buildString(n.left, indent + 2, sb); buildString(n.right, indent + 2, sb); } } PFK (Föreläsning 8) VT 2017 Traversering i postorder Traversering i inorder Exempel Exempel Om vi har ett binärt träd som representerar aritmetiska uttryck kan vi beräkna trädets värde som en postorder-genomgång. Subträdens värden beräknas först. Därefter ”besöks” roten genom att dess operator appliceras på subträdens värde. 26 / 32 Om vi har ett binärt sökträd får vi elementen i stigande ordning vid en inorder-genomgång. Mona * + Hans * 1 + Ann 2 1+2*3 PFK (Föreläsning 8) 3 1 Nora 3 Karl Tora 2 (1 + 2) * 3 VT 2017 27 / 32 PFK (Föreläsning 8) VT 2017 28 / 32 Nivå- för nivåtraversering Träd, binära träd, binära sökträd Nivå- för nivåtraversering kan implementeras med hjälp av en kö: Exempel på vad du ska kunna Förklara begreppen träd och binära träd samt begrepp som rot, löv, subträd, förälder, barn. skapa en tom kö lägg in roten i kön så länge kön inte är tom tag ut och behandla första noden (actNode) om actNode.left != null lägg in actNode.left i kön om actNode.right != null lägg in actNode.right i kön PFK (Föreläsning 8) Förklara begreppen nivå/djup för en nod och begreppet höjd för träd. Implementera träd med en länkad datastruktur. Beskriva olika sätt att traversera träd: preorder, inorder, postorder. Implementera operationer på träd, speciellt rekursiva metoder. VT 2017 29 / 32 PFK (Föreläsning 8) VT 2017 Datorlaboration 4 Datorlaboration 4, forts Binära sökträd Insättning i binära sökträd Implementera en egen generisk klass för binära sökträd. Nycklarna i vänster subträd < roten < nycklarna i höger subträd Dubbletter är ej tillåtna. 5 2 Nya noder sätts alltid in som löv. Insättning börjar med en (förhoppningsvis) misslyckad sökning. 9 4 30 / 32 6 Exempel: Sätt in 3. 8 I flera fall blir det en (kort) publik metod som anropar en privat rekursiv metod. I en av metoderna ska ett nytt träd byggas upp från värden i en vektor. Hämta inspiration från den rekursiva algoritmen för binärsökning. Rita för att förstå vad som händer i programmet! 2 9 4 Misslyckad sökning! Sätt in 3 här. Innehåll: binära sökträd, rekursion, länkad struktur. PFK (Föreläsning 8) 5 Börja här: Tips: VT 2017 31 / 32 PFK (Föreläsning 8) 6 8 VT 2017 32 / 32