Bäume - Universität Paderborn

Werbung
Bäume
Universität Paderborn
Prof. Dr. Heike Wehrheim
Eine rekursive Datenstruktur mit rekursiven
Methoden
Wichtigstes Beispiel in der Informatik: binärer Baum
M heißt Binärbaum über G, wenn gilt:
G: Grundmenge
1. M ist die leere Menge, also M = ∅
(wobei ∅ das leere Element repräsentiert)
oder
2. Es gibt ein g ∈ G mit: M = (B1, g, B2) und
B1 und B2 sind Binärbäume über G.
GP1 – WS 08/09
599
Beispiel
Universität Paderborn
Prof. Dr. Heike Wehrheim
Grundmenge G = natürliche Zahlen
= {1, 2, 3, ... }
M1 = (∅, 2, ∅) ist Binärbaum über G
M2 = (M1, 3, ∅) ist Binärbaum über G
M3 = (M1, 5, M2) ist Binärbaum über G
M4 = (M3, 2, M2) ist Binärbaum über G
M4 = (M3, 2, M2) = ((M1, 5, M2), 2, (M1, 3, ∅)) =
((M1, 5, (M1, 3, ∅)), 2, (M1, 3, ∅)) =
(((∅, 2, ∅), 5, ((∅, 2, ∅), 3, ∅)), 2, ((∅, 2, ∅), 3,
∅))
GP1 – WS 08/09
600
Universität Paderborn
Prof. Dr. Heike Wehrheim
Beispiel
M4 = (M3, 2, M2)
M2 = (M1, 3, ∅)
M3 = (M1, 5, M2)
2
5
M3
3
M1
M2
M2
M1
2
2
M3
5
M3
5
M2
M1
M1
M2
3
3
M2
M1
M2
M1
GP1 – WS 08/09
601
Universität Paderborn
Prof. Dr. Heike Wehrheim
Weiter…
M1 = (∅, 2, ∅)
M4=((M1, 5, (M1, 3, ∅)), 2, (M1, 3, ∅))
2
2
M3
5
Jetzt noch M1 ersetzen durch 2
M1
M2
3
3
M2
2
M1
M3
5
2
M1
M2
3
3
M2
2
GP1 – WS 08/09
2
602
Struktur
Universität Paderborn
Prof. Dr. Heike Wehrheim
ƒ Es entsteht eine Hierarchie, gefüllt mit Elementen
der Grundmenge
Jeder Knoten hat einen Wert und einen linken und
rechten Teilbaum, der ggf. leer ist
ƒ Knoten ohne Nachfolger sind Blattknoten oder
Blätter
Knoten mit Nachfolgern heißen innere Knoten
Eigenes Beispiel: an der Tafel
GP1 – WS 08/09
603
Benutzung in Programmen
Universität Paderborn
Prof. Dr. Heike Wehrheim
Aufbau als verzeigerte
Struktur (wie bei linearen
Listen)
Pro Knoten drei Einträge:
1. Verweis auf den linken
Teilbaum (null, wenn
Teilbaum = ∅)
2. Gespeicherter Wert der
Grundmenge
3. Verweis auf den rechten
Teilbaum (null, wenn
Teilbaum = ∅)
GP1 – WS 08/09
604
In Java
Universität Paderborn
Prof. Dr. Heike Wehrheim
Zahlen (int) als Grundmenge:
class BinTree {
private BinTree left, right;
private int value;
// Konstruktor für innere Knoten
BinTree (BinTree l, int v, BinTree r)
{ left = l; value = v; right = r; }
// Konstruktor für Blattknoten
BinTree (int v)
{ left = null; value = v; right = null; }
...}
GP1 – WS 08/09
605
Universität Paderborn
Prof. Dr. Heike Wehrheim
Anschaulich
6
3
steht also für
:BinTree
left:
right: null
value: 6
GP1 – WS 08/09
:BinTree
left: null
right: null
value: 3
606
Beispielaufbau eines Baumes
BinTree
BinTree
BinTree
BinTree
BinTree
BinTree
b1
b2
b3
b4
b5
b6
=
=
=
=
=
=
new
new
new
new
new
new
BinTree(2);
BinTree(3);
BinTree(b1, 6, b2);
BinTree(4);
BinTree(b4, 8, null);
BinTree(b5, 3, b3);
oder in einer einzigen Anweisung:
BinTree b6 =
new BinTree(
new BinTree(
new BinTree(4),
8,
null),
3,
new BinTree(
new BinTree(2),
6,
new BinTree(3))
);
GP1 – WS 08/09
Universität Paderborn
Prof. Dr. Heike Wehrheim
3
8
4
6
2
3
607
Baumdurchläufe
Universität Paderborn
Prof. Dr. Heike Wehrheim
Drei Arten, je nach Position des eigenen Wertes:
INFIX = Erst linken Teilbaum ausgeben, dann den eigenen
Wert, dann rechten Teilbaum ausgeben
PREFIX = Erst den eigenen Wert ausgeben, dann linken
Teilbaum, dann rechten Teilbaum ausgeben
POSTFIX = Erst den linken Teilbaum ausgeben, dann rechten
Teilbaum, dann eigenen Wert ausgeben
Im Beispielbaum (von letzter Folie):
INFIX: 4 - 8 - 3 - 2 - 6 - 3
PREFIX: 3 - 8 - 4 - 6 - 2 - 3
POSTFIX: 4 - 8 -2 - 3 - 6 - 3
zusätzlich mit Klammern, also in der Form
INFIX: ( linker Teilbaum - eigener Wert - rechter Teilbaum )
INFIX: ( ( ( 4 ) 8 ( ) ) 3 ( ( 2 ) 6 ( 3 ) ) )
GP1 – WS 08/09
608
Infix-Ausgabe in Java
Universität Paderborn
Prof. Dr. Heike Wehrheim
void inFix() {
// Ausgabe in Infix-Form,
// geklammert
System.out.print(“(“);
// Drucke linken Teilbaum
if (left !=null) left.inFix();
// Drucke eigenen Wert
System.out.print(value);
// Drucke rechten Teilbaum
if (right !=null) right.inFix();
System.out.print(“)”);
}
Aufruf etwa durch
b6.inFix();
BinTreeTest.java
produziert
(((4)8)3((2)6(3)))
GP1 – WS 08/09
609
Postfix-Ausgabe
Universität Paderborn
Prof. Dr. Heike Wehrheim
void postFix() {
// Ausgabe in Postfix-Form
// ohne Klammern
// Drucke linken Teilbaum
if (left !=null) left.postFix();
// Drucke rechten Teilbaum
if (right !=null) right.postFix();
// Drucke eigenen Wert
System.out.print(value);
}
Dann produziert b6.postFix();
die Ausgabe 482363
GP1 – WS 08/09
610
Wichtig für Rekursion
Universität Paderborn
Prof. Dr. Heike Wehrheim
Abbruchbedingung:
Hier: kein rechter oder linker Unterbaum mehr da
(left == null, right == null)
„Auf Abbruch hinarbeiten“: Methode wird für Objekte
aufgerufen, die näher am Abbruch sind, nämlich
tiefer im Baum, näher an den Blättern
GP1 – WS 08/09
611
Weiteres Beispiel
Universität Paderborn
Prof. Dr. Heike Wehrheim
Berechnung der Summe aller Werte von Knoten
int summeKnoten () {
int summe = 0;
summe += value;
if (left != null)
summe += left.summeKnoten();
if (right != null)
summe += right.summeKnoten();
return summe;
}
BinTreeSummenTest.java
GP1 – WS 08/09
612
Eigene Aufgabe
Universität Paderborn
Prof. Dr. Heike Wehrheim
Schreiben Sie eine rekursive Methode, die die
Knoten eines Binärbaumes zählt.
BinTreeSummenTest.java
GP1 – WS 08/09
613
Bedeutung von Binärbäumen
Universität Paderborn
Prof. Dr. Heike Wehrheim
ƒ Binäre Suchbäume zum (sehr) schnellen Suchen
in großen Datenmengen
Werte im linken Teilbaum kleiner als der eigene
Wert; dieser wiederum kleiner (kleiner gleich) als
die Werte im rechten Teilbaum
INFIX-Form entspricht der sortierten Wertefolge
ƒ Bäume, die Rechenausdrücke beschreiben
Innere Knoten sind mit Operationen versehen;
Blätter entsprechen den Argumenten eines
Ausdrucks
Schnelle Auswertung des Ausdrucks im Baum
INFIX-Form entspricht der normalen geklammerten
Schreibweise
GP1 – WS 08/09
614
Universität Paderborn
Prof. Dr. Heike Wehrheim
Binäre Suchbäume
Für jeden Knoten im Baum gilt:
ƒ Die Werte im linken Teilbaum sind kleiner als Wert
des Knotens
ƒ Die Werte im rechten Teilbaum sind größer (gleich)
dem Wert des Knotens
ƒ Doppelte Werte können zugelassen sein oder auch
nicht (hier: keine doppelten Werte)
Beispiel:
12
8
34
10
GP1 – WS 08/09
23
46
615
Universität Paderborn
Prof. Dr. Heike Wehrheim
Aufbau
Sortierter Baum (über Zahlen)
public class SortedBinTree {
int value;
SortedBinTree left;
SortedBinTree right;
SortedBinTree (int v) {
value = v;
left = null;
right = null;
}
GP1 – WS 08/09
Warum kein Konstruktor,
der Werte für left und right
als Parameter bekommt?
616
Aufbau II
Universität Paderborn
Prof. Dr. Heike Wehrheim
Aufbau muss einen sortierten Baum ergeben, Benutzung
einer Methode insert
Was ist zu beachten?
ƒ Der einzufügende Werte kann schon drin sein -> dann nicht
nochmal einfügen
ƒ Einfügen startet bei Wurzelknoten:
einzufügender Wert kleiner als Wert des Wurzelknotens ->
links einfügen
einzufügender Wert größer als Wert des Wurzelknotens ->
rechts einfügen
Achtung: prüfen, ob linker bzw. rechter Teilbaum überhaupt
existiert, sonst nur neuen Knoten mit einzufügendem Wert
erzeugen und entsprechend einhängen
ƒ Einfügen in Teilbaum funktioniert wie Einfügen an
Wurzelknoten -> rekursive Methode
GP1 – WS 08/09
617
insert
Universität Paderborn
Prof. Dr. Heike Wehrheim
void insert (int v) {
if (v == value)
System.out.println("Value already existed.");
else
if (v < value) {
links einfügen
if (left!= null)
left.insert(v);
Links neuen
else
Knoten
left = new SortedBinTree(v);
erzeugen
}
else {
if (right!= null)
rechts einfügen
right.insert(v);
else
right = new SortedBinTree(v);
}
}
Rechts neuen Knoten
SortedBinTree.java
erzeugen
GP1 – WS 08/09
618
Fragen
Universität Paderborn
Prof. Dr. Heike Wehrheim
Gegeben
class BinTree {
private BinTree left, right;
private int value;
BinTree(BinTree l, int v, BinTree r) {
left = l; value = v; right = r;
}
}
Welchen Baumdurchlauf druckt die folgende Objektmethode
xxFix() von BinTree?
void xxFix() {
if (right != null) right.xxFix();
System.out.print(value);
if (left!= null) left.xxFix();
}
(a) Prefix (b) Infix (c) Postfix (d) Prefix rückwärts
(e) Infix rückwärts (f) Postfix rückwärts
GP1 – WS 08/09
619
Fragen II
Universität Paderborn
Prof. Dr. Heike Wehrheim
Bestimmen Sie zu dem Infix-Ausdruck
(5+7)*(44-(8465/35))
den dazugehörigen Postfix-Ausdruck.
Malen Sie sich dazu zuerst den Baum auf, den der
Infix-Ausdruck repräsentiert.
GP1 – WS 08/09
620
Generische Klassen
Universität Paderborn
Prof. Dr. Heike Wehrheim
Generische Klassen sind Schablonen für konkrete
Klassen, bei denen Komponententypen noch offen
gelassen werden und durch Typvariablen
dargestellt werden.
Eine konkrete Klasse entsteht durch Instantiierung
der Typvariablen mit konkreten Typen, den
Typargumenten.
Kennen wir im Prinzip schon:
HashMap<K,V> : Typen von Schlüssel und Werten
werden durch Typvariablen K und V repräsentiert
HashMap<Integer,String> : eine konkrete
Klasse mit Typargument Integer für K und
String für V
GP1 – WS 08/09
621
Einführungsbeispiel
Universität Paderborn
Prof. Dr. Heike Wehrheim
Klasse zum Abspeichern von Paaren von Integern
class IntegerPaar {
private Integer erster;
private Integer zweiter;
IntegerPaar(Integer e, Integer z) {
erster = e;
zweiter = z;
}
public void setErster (Integer e) {
erster = e;
}
public Integer getErster () {
return erster;
} … // gleiches für zweiter
}
GP1 – WS 08/09
622
Und nun …
Universität Paderborn
Prof. Dr. Heike Wehrheim
Klasse zum Abspeichern von Paaren von Strings
class StringPaar {
private String erster;
private String zweiter;
StringPaar(String e, String z) {
erster = e;
zweiter = z;
}
public void setErster (String e) {
erster = e;
}
public String getErster () {
return erster;
}… // gleiches für zweiter
}
GP1 – WS 08/09
623
Und nun …
Universität Paderborn
Prof. Dr. Heike Wehrheim
Klassen zum Abspeichern von Paaren von Personen,
Rechtecken, Hunden, …
Jedes Mal:
ƒ Klasse kopieren, Typen der Objektvariablen ändern
(und entsprechend Parameter der Methoden)
ƒ Möchte man eine Methode von …Paar ändern/hinzufügen,
muß man sie 1,2,3, … -mal ändern/hinzufügen
Stattdessen sinnvoller:
ƒ eine Klassenschablone angeben,
ƒ darin konkreten Typ durch Typvariable ersetzen
ƒ bei Erzeugung von Objekten: konkreten Typ als
Typargument angeben; wird für Typvariable eingesetzt
GP1 – WS 08/09
624
Am Beispiel Paar
Universität Paderborn
Prof. Dr. Heike Wehrheim
class Paar<T> {
private T erster;
private T zweiter;
Paar(T e, T z) {
erster = e;
zweiter = z;
}
public void setErster (T e) {
erster = e;
}
public T getErster () {
return erster;
}… /gleiches für zweiter
}
GP1 – WS 08/09
625
Dann geht folgendes …
Universität Paderborn
Prof. Dr. Heike Wehrheim
Paar<Integer> pi =
new Paar<Integer>(new Integer(4),
new Integer(32));
Paar<String> ps =
new Paar<String>("bar","foo");
Paar<Student> pst = new Paar<Student>(… , …);
usw.
Und folgendes ist möglich:
Integer i = pi.getErster();
ps.setErster(“bla”);
GP1 – WS 08/09
626
Warum nicht Object?
Universität Paderborn
Prof. Dr. Heike Wehrheim
Was wäre, wenn wir Paar mit Object als Typ definiert hätten?
class ObjectPaar {
private Object erster;
private Object zweiter;
ObjectPaar(Object e, Object z) {
erster = e;
zweiter = z;
} …
}
Möglich wäre nun
ObjectPaar op1 = new
ObjectPaar(“hallo“,“bla“);
Paar<T> erlaubt
das nicht!
aber auch
ObjectPaar op2 = new ObjectPaar(“foo”,
new Integer(42));
GP1 – WS 08/09
627
Ausserdem …
Universität Paderborn
Prof. Dr. Heike Wehrheim
Zum Zugreifen nun Typecast nötig
String s = (String) op1.getErster();
Das kann Fehler liefern:
Integer s = (Integer) op2.getErster();
(ClassCastException)
Verwendung von generischen Datenstrukturen:
ƒ Compiler kann zur Übersetzungszeit nicht
passende Typen überprüfen
ƒ Typsicherheit erhöht
GP1 – WS 08/09
628
Universität Paderborn
Prof. Dr. Heike Wehrheim
Mehrere Typvariablen
Brauchen eine Paar-Klasse mit unterschiedlichen Elementen:
class Paar2<T,U> {
private T erster;
private U zweiter;
Paar2(T e, U z) {
erster = e;
zweiter = z;
}
public void setErster (T e) {
erster = e;
}
public void setZweiter (U z) {
zweiter = z;
}
public T getErster () {
return erster;
}
public U getZweiter() {
return zweiter;
}
}
GP1 – WS 08/09
629
Und dann …
Universität Paderborn
Prof. Dr. Heike Wehrheim
Konkrete Klasse:
Paar2<Integer, String> p1 =
new Paar2<Integer,String>(2,“foo“);
Integer i = p1.getErster();
// ok
Integer j = p1.getZweiter();
// Fehler bei Übersetzung
Auch möglich:
Paar2<Integer,Paar2<Boolean,String>> …
(Paar2 taucht selber als Typargument auf)
GP1 – WS 08/09
630
Anwendungsbeispiel
Universität Paderborn
Prof. Dr. Heike Wehrheim
Generische Klasse für Binärbäume:
class BinTree<T> {
private BinTree<T> left;
private BinTree<T> right;
private T value;
BinTree(T v, BinTree<T> l, BinTree<T> r) {
value = v;
left = l;
right = r;
}
}
Wie sehen nun die Methoden setLeft, getLeft,
setValue und getValue aus?
GP1 – WS 08/09
631
Herunterladen