Föreläsning 8 Datastrukturer i kursen Träd Träd – rekursiv definition

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