Algorithmen und Datenstrukturen CS1017 - Benutzer

Werbung
Algorithmen und
Datenstrukturen CS1017
Th. Letschert
TH Mittelhessen Gießen
University of Applied Sciences
Datenstrukturen II: Bäume
– Datenstruktur Baum in der Java-API
– Bäume traversieren
– Anwendung: Hierarchisch strukturierte Daten darstellen
– Anwendung: Suchbäume / Daten Speichern und Suchen
Datentypen und Datenstrukturen in der Java-API
Collection Framework
– Das Java-API enthält – im package java.util – das Collection-Framework:
Klassen die bei der Organisation von Daten helfen
– Das Collection-Framework bietet fertige Lösungen für
 Listen
 Mengen
 Abbildungen
 … und vieles andere mehr
– Das Collection Framework enthält
Listen, Mengen, Abbildungen, ...
 In Form von Interfaces / Datentypen und
 In Form von Klassen die die Interfaces
implementieren / (abstrakte) Datentypen
– Das Collection Framework nutzt
diverse Datenstrukturen bei der Implementierung der Klassen
– Alle ernsthaften Programmiersprachen haben ihr Collection Framework,
es sei denn die entsprechende Funktionalität ist direkt in der Sprache verfügbar
Seite 2
Datentyp Abbildung (Map)
Abbildung (Map) als Datentyp
Eine Map realisiert die Zuordnung von Objekten zu einem anderen Objekt
– Einem eindeutigen Schlüsselwert (key)
– kann ein Wert (value) zugeordnet werden
– Außerordentlich wichtiger Datentyp
Alle modernen Programmiersprachen bieten eine Map-Implementierung
– die eventuell anders genannt wird (assoziatives Array, Dictionary, etc.)
[http://en.wikipedia.org/wiki/Comparison_of_programming_languages_%28mapping%29]
– in Skript-Sprachen sind Maps elementares Sprachkonstrukt
– in Java Bestandteil der Standard-Bibliothek (API)
Beispiel
public static void main(String[] args) {
Map<String, Integer> phoneBook = new TreeMap<>();
phoneBook.put("Karla ",
phoneBook.put("Egon ",
phoneBook.put("Dagmar",
phoneBook.put("Hugo ",
4711);
1815);
1234);
4433);
Dagmar
Egon
Hugo
Karla
for (String key: phoneBook.keySet()) {
System.out.println(key + " -> " + phoneBook.get(key));
}
}
Seite 3
->
->
->
->
1234
1815
4433
4711
Ausgabe nach
Schlüsseln sortiert
Datentyp Map in der Java-API
Interface java.util.Map
Klasse TreeMap
Implementierung des Datentyps Map mit Hilfe der Datenstruktur Baum
Die Ordnung der Schlüssel (compareTo) wird bei der Speicherung berücksichtigt
derart dass Speicher- und Suchfunktionen optimiert sind.
Klasse HashMap
Implementierung der des Datentyps Map mit Hilfe der Datenstruktur Hash-Tabelle
Map
<<interface>>
SortedMap <<interface>>
Java-API:
Maps und ihre
Implementierungen
AbstractMap
TreeMap
HashMap
Seite 4
Datentyp Map in der Java-API
Map, TreeMap, HashMap
A
B
C
D
K
L
→
→
→
→
→
→
X
Y
Z
U
V
W
Datentyp Map:
Konzept einer
Abbildung
C
Z
K
V
A
X
TreeMap
ImplementierungsVariante für Abbildungen:
Datenstruktur Baum
B
Y
L
W
D
U
D
U
C
Z
HashMap:
ImplementierungsVariante für Abbildungen
01
02
03
04
K
V
A
X
B
Y
Seite 5
L
W
Datentyp Menge (Set)
Menge (Set)
Ein Set realisiert Kollektionen von Objekten die
– Nicht mehrfach das Gleiche enthalten
– Keine vom Benutzer definierte Ordnung haben
Einsatz
– Wiederholungen sollen vermieden werden
– Der Verzicht auf eine benutzerdefinierte Ordnung gibt die Möglichkeit zu effizienter interner
Darstellung.
Listen haben haben eine vom Benutzer der Liste definierte Ordnung. Diese muss von der
Implementierung respektiert werden. Bei Mengen ist die Implementierung frei darin, die
Elemente nach Belieben zu ordnen.
Beispiel
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
for(String s : new String[]{ "eins", "zwei", "drei", "vier", "fuenf",
"sechs", "sieben", "acht", "neun", "zehn",
"elf", "zwoelf", "dreizehn"}
) {
set.add(s);
}
}
for (String s: set) {
System.out.println(s);
}
Sortieren mit einem Set
Seite 6
Datentyp Set in der Java-API
Interface java.util.Set
Klasse TreeSet
Implementierung des Datentyps Set mit Hilfe der Datenstruktur Baum
Die Ordnung der Elemente (compareTo) wird bei der Speicherung berücksichtigt
derart dass Speicher- und Suchfunktionen optimiert sind.
Klasse HashSet
Implementierung der des Datentyps Set mit Hilfe der Datenstruktur Hash-Tabelle
Set
<<interface>>
SortedSet <<interface>>
Java-API:
Sets und ihre
Implementierungen
AbstractSet
TreeSet
HashSet
Seite 7
Datenstruktur Baum
Baum – als besonderer Graph
Ein Baum ist eine Sonderform eines Graphen
Definitionen
– Ein gerichteter Graph G = (V, E) besteht aus einer endlichen Menge V von Knoten und einer
Menge E von geordneten Paaren (a, b) mit a, b ∈ V , genannt Kanten.
– Zwei Knoten eines Graphen heißen zusammenhängend, wenn sie durch einen Kantenzug
verbunden sind.
– Ein Graph G heißt zusammenhängend, wenn zwei beliebig herausgegriffene Knoten stets
zusammenhängend sind.
– Ein zusammenhängender Graph, der keinen Kreis enthält, heißt ein Baum.
– Wird bei einem Baum irgend ein Knoten besonders ausgezeichnet, dann nennt man den Baum
einen Wurzelbaum und den ausgezeichneten Knoten die Wurzel.
– In einem Wurzelbaum haben alle Kanten eine Richtung: weg von der Wurzel
– Die Beziehungen zwischen verbundenen Knoten werden mit
 Vorgänger / Nachfolger, bzw. Eltern / Kinder
bezeichnet
– In einem geordneten Baum haben alle Nachfolger eine definierte Reihenfolge
– Ein Blatt ist ein Knoten ohne Nachfolger
– Ein innerer Knoten ist ein Knoten der kein Blatt ist.
Mit Baum meinen wir im Allgemeinen einen geordneten Wurzelbaum
Seite 8
vergl. Metz, Diskrete Mathematik
Datenstruktur Baum
Baum – als rekursiv definierte Datenstruktur
Ein Baum ist eine rekursive Datenstruktur.
Baum: rekursive Definition
– Basis:
Jeder Knoten ist ein Baum
– Rekursion: Sei r ein Knoten und T1, .. Tn Bäume mit den Wurzeln r1, .. rn,
dann ist der Graph bestehend aus r und T1, .. Tn ein Baum T mit:
 r ist die Wurzel von T
 T enthält jeweils eine neue Kante von r nach ri ,
i ∊ { 1 , .. n}
r
r
r1
r2
r3
T1
T2
T3
r1
r2
r3
T1
T2
T3
T
Bäume sind nicht irgendeine rekursive
Datenstruktur, sie sind das Musterbeispiel
rekursiver Datenstrukturen!
Strukturelle Rekursion auf Bäumen: Da Bäume eine rekursive Struktur
haben, können leicht rekursive Funktionen auf ihnen definiert werden.
Seite 9
Datenstruktur Baum: Verwendung
Einsatz von Bäumen
Bäume sind eine wichtige Datenstruktur mit vielen Anwendungen.
Es gibt kaum eine sinnvolle Verwendung eines Datentyps Baum
aber die Datenstruktur Baum wird bei der Implementierung diverser
anderer Datentypen genutzt.
Wichtige Beispiele für den Einsatz der Datenstruktur Baum
1) Bäume werden genutzt um hierarchische Strukturen beliebiger Art darzustellen
Beispiel: Syntaxbäume, Stammbäume, ….
2) Bäume werden sehr oft benutzt um Datenabstraktionen „mit Suchfunktion“
zu implementieren, z.B.:
 Abbildung und
 Menge
 Tabelle / Relation
 ...
In der Java-API gibt es beispielsweise Baum-basierte Abbildungen und Mengen:
 java.util.TreeMap,
 java.util.TreeSet
Seite 10
Datenstruktur Baum: Verwendung
Einsatz von Bäumen
Bäume sind eine wichtige Datenstruktur mit vielen Anwendungen.
Beispiel: Syntaxbäume
Bäume werden benutzt um die Struktur von sprachlichen Ausdrücken darzulegen.
Compiler erzeugen Syntaxbäume aus Programmtext. Bei der Verarbeitung natürlicher
Sprachen werden (weit komplexere) Syntaxbäume erzeugt.
*
2 * (3 + 4*5)
+
2
*
3
4
Seite 11
5
Datenstruktur Baum: Implementierung
Syntax-Bäume als rekursive Menge* – Implementierung
Ein Syntax-Baum für arithmetische Ausdrücke ist entweder
– Ein Blatt
 mit einer Zahl als Wert
oder
– Ein innerer Knoten mit
 einem Operator und
 zwei Bäumen als Operanden
interface ExpTree {
enum Op {plus, minus, mult, div }
double compute();
}
Ausdrucksbäume können mit compute
ausgewertet werden.
Der Typ der Operatoren wird hier definiert.
class ValNode implements ExpTree {
Ein „Blatt“ des Baums besteht aus einer
Zahl als Wert. Die Auswertung von
Blättern liefert ihren Wert.
double val;
public ValNode(double val) { this.val = val; }
}
@Override
public double compute() { return val; }
* der Kürze wegen sagen wir (falsch) „rekursive Menge“ und
meinen damit stets korrekt „rekursiv definierte Menge“
Seite 12
Datenstruktur Baum: Implementierung
Syntax-Bäume als rekursive Menge – Implementierung
class OpNode implements ExpTree {
Op op;
ExpTree left;
ExpTree right;
Innere Knoten haben
haben einen Operator
und zwei Unterbäume.
public OpNode(Op op, ExpTree left, ExpTree right) {
this.op = op; this.left = left; this.right = right;
}
@Override
public double compute() {
return toFun(op).apply( left.compute(), right.compute() );
}
}
static BiFunction<Double, Double,
switch (op) {
case plus : return (Double x,
case minus : return (Double x,
case mult : return (Double x,
case div :
return (Double x,
default:
return null;
}
}
Bei der Auswertung
werden die Unterbäume
ausgewertet und die
berechneten Werte
entsprechend dem
Operator verknüpft.
Double> toFun(Op op) {
Double
Double
Double
Double
y)
y)
y)
y)
->
->
->
->
x+y
x-y
x*y
x/y
;
;
;
;
Seite 13
Diese Hilfs-Funktion liefert die
Verknüpfungsfunktion zu
einem Operator.
Datenstruktur Baum: Implementierung / Verwendung
Einsatz von Bäumen
Beispiel: Ausdrücke als (abstrakter) Datentyp, der intern die Datenstruktur Baum verwendet
class Expression {
private interface ExpTree { … }
private static class OpNode implements ExpTree { … }
private static class ValNode implements ExpTree { … }
Datenstruktur-Definitionen wie oben,
eingepackt in eine „Hüll-Klasse“ die die
Nutzer-Sicht auf die Bäume darstellt.
private ExpTree root = null;
public Expression(double v) {
root = new ValNode(v);
}
Konstruktoren und Auswertefunktion
public Expression (Expression e1, char c, Expression e2) {
this.root = new OpNode(toOp(c), e1.root, e2.root);
}
public double compute() {
return root.compute();
}
interface ExpTree {...
private static ExpTree.Op toOp(char c) {
switch (c) {
case '+' : return ExpTree.Op.plus;
case '-' : return ExpTree.Op.minus;
case '*' : return ExpTree.Op.mult;
case '/' : return ExpTree.Op.div;
default:
return null;
}
}
}
Seite 14
Datenstruktur Baum: Implementierung
Varianten der Implementierung der Datenstruktur Baum
Implementierung mit Zeigern
Blätter und Knoten des Baums werden über Zeiger (Pointer) miteinander verbunden
(siehe auch Beispiele oben und in Foliensatz 3)
Implementierung mit Arrays
Blätter und Knoten des Baums werden in Arrays gespeichert, die Verbindung erfolgt über
Indizes
Implementierung mit Matrix
Bäume sind eine Sonderform der Graphen, sie können darum wie diese in einer
Adjazenzmatrix gespeichert werden.
Seite 15
Implementierung von Bäumen
Zeiger-Implementierung von Bäumen
– Sortierte Bäume: Knoten und Blätter haben unterschiedliche Typen (Sorten)
 Definiere eine Basisklasse (oder Interface) für das Gemeinsame aller Knoten
 Definiere eine abgeleitete Klasse für jede Art von Knoten
D.h. Bäume werden als rekursive Menge mit OO-Mitteln implementiert
abstract class Node<T> {
. . .
}
InnerNode
class InnerNode<T> extends Node<T> {
private List<Node<T>> children;
. . .
}
Leaf
class Leaf<T> extends Node<T> {
private T value;
. . .
}
Node <<abstract>>
Vergl. Kompositum Muster
InnerNode
Leaf
Klassendiagramm
Seite 16
Implementierung von Bäumen
Beispiel: Syntax-Bäume als sortierte Zeigerbäume
Beispiel mit mehreren Konstruktionsfunktionen:
Ein Baum ist
–
–
–
–
–
eine Konstante mit einem Wert
ein Add-Knoten mit zwei Unter-Bäumen
ein Add-Knoten mit zwei Unter-Bäumen
ein Mult-Knoten mit zwei Unter-Bäumen
ein Div-Knoten mit zwei Unter-Bäumen
oder
oder
oder
oder
Tree <<interface>>
Mult
left
Sub
Const
4
Const
Const
5
Const
2
InnerNode
right
Sub
Add
Mult
Implementierung: Eine
Baum zu (4-2)*5
Klasse für jede Sorte von
Knoten
Seite 17
Div
Implementierung von Bäumen
Beispiel: ADT Expression implementiert mit der Datenstruktur Baum
class Expression {
public Expression(Double v) {
this.root = new Const(v);
}
Datentyp, die „Hüll-Klasse“ die die
Nutzer-Sicht darstellt.
public Expression add(Expression other) {
this.root = new Add(this.root, other.root);
return this;
}
public Expression sub(Expression other) {
this.root = new Sub(this.root, other.root);
return this;
}
public Expression mult(Expression other) {
this.root = new Mult(this.root, other.root);
return this;
}
public Expression div(Expression other) {
this.root = new Div(this.root, other.root);
return this;
}
public double compute() {
return root.compute();
}
}
private Tree root = null;
Datenstruktur, die Implementierung
der Funktionalität
private interface Tree {...}
private
private
private
private
private
private
Seite 18
static
static
static
static
static
static
abstract class InnerNode implements Tree {...}
class Add extends InnerNode {...}
class Sub extends InnerNode {...}
class Mult extends InnerNode {...}
class Div extends InnerNode {...}
class Const implements Tree {...}
Implementierung von Bäumen
Beispiel: ADT Expression implementiert mit der Datenstruktur Baum fortgesetzt
private interface Tree { double compute(); }
private static abstract class InnerNode implements Tree {
Tree left;
Tree right;
InnerNode(Tree left, Tree right) {
this.left = left;
this.right = right;
}
}
private static class Add extends InnerNode {
Add(Tree left, Tree right) {
super(left, right);
}
@Override
public double compute() {
return left.compute() + right.compute();
}
}
… Sub, Mult, Div entsprechend …
private static class Const implements Tree {
double value;
Const(double value) { this.value = value; }
@Override
public double compute() { return value; }
}
Seite 19
Die privaten Definitionen zur
Konstruktion der Datenstrukturen
Binäre Bäume
Binärer Baum
Ein binärer Baum (Binärbaum) ist ein geordneter gerichteter Wurzelbaum bei dem jeder Knoten
maximal zwei Nachfolger hat: einen
– linken Unterbaum und einen
– rechten Unterbaum
A
A
A
A
B
C
B
B
D
E
F
vier unterschiedliche Binärbäume
Seite 20
G
Binäre Bäume
Implementierung binärer Bäume
– Zeigerdarstellung
Mit Zeigern (Pointern) verbundene Knoten
A
B
v: A, l: , r:
C
D
E
F
G
v: B, l: , r:
v: C, l: , r:
v: D, l: , r:
v: E, l: , r:
v: F, l: , r:
v: G, l: , r:
Zeiger-Implementierung
Seite 21
Binäre Bäume
Implementierung binärer Bäume
– Tabellendarstellung
Mit Indizes verbundene Tabelleneinträge
A
B
C
D
E
F
0
v: A
l:
1
r:
2
1
v: B
l: -1
r:
3
2
v: C
l: -1
r:
4
3
v: D
l: -1
r: -1
4
v: E
l:
r:
5
v: F
l: -1
r: -1
6
v: G
l: -1
r: -1
G
Tabellen-Implementierung
Seite 22
5
6
Binäre Bäume
Implementierung binärer Bäume
– Tupeldarstellung
Baum als verschachtelte Tupel: (Wurzel, linker Unterbaum, rechter Unterbaum)
A
(A,
B
(B,(),
C
(D,()())),
(C, (),
(E, (F,(),(),
D
(G,(),()))))
E
F
G
Tupel-Darstellung
Seite 23
Baum-Traversierungen
Traversieren (Durchlaufen) von Bäumen
Die Knoten eines Baums können in unterschiedlicher Reihenfolge durchlaufen werden.
Rekursive Verfahren (Zuerst-in-die-Tiefe / Depth-First) sind:
– Preorder: erst Mitte, dann Links, dann Rechts
– Inorder: erst Links, dann Mitte, dann Rechts
– Postorder: erst Links, dann Rechts, dann Mitte
A
B
A
C
B
C
PreorderTraversierung
D
E
F
PostorderTraversierung
D
G
E
F
Besuchsreihenfolge: A B D C E F G
G
Besuchsreihenfolge: D B F G E C A
Seite 24
Baum-Traversierungen
Traversieren (Durchlaufen) von Bäumen
Beispiel: Rekursive (Depth-First) Traversierung zur Ausdrucksauswertung
private interface Tree { double compute(); }
...
private static class Add extends InnerNode {
Add(Tree left, Tree right) { super(left, right); }
@Override
public double compute() {
return left.compute() + right.compute();
}
class Expression {
. . .
private Tree root = null;
public double compute() {
return root.compute();
}
@Override
public String toString() {
return root.toString();
}
}
@Override
public String toString() {
return "(" + left.toString() + " + " + right.toString() + ")";
}
...
private static class Const implements Tree {
double value;
Const(double value) { this.value = value; }
}
@Override
public double compute() { return value; }
}
@Override
public String toString() { return ""+value; }
Seite 25
Baum-Traversierungen
Traversierung von Bäumen
Beobachtung:
Die Traversierung eines Baums zum Berechnen des Werts oder der String-Darstellung
hat zwei Grundbestandteile
– Durchlaufen des Baums:
Knoten nach Knoten besuchen
– Ausführung einer Aktion:
Anwenden einer Methode / Funktion auf den gerade besuchten Knoten
Verallgemeinerung der Traversierung:
Die Traversierung eines Baums kann darum getrennt werden in
– einen (fixen) Algorithmus zum Druchlaufen der Knoten und
– einem (wechselnden) Algorithmus der auf die knoten angewendet wird.
Es liegt nahe dies zu organisieren als
– Traversierungsfunktion, die
– Eine Parameter hat, der die auszuführende Aktion beschreibt
Seite 26
Besuchermuster
Baum-Traversierung nach dem Besuchermuster
• Muster : Vorgabe / allgemeines Beispiel wie eine normierte Problemstellung zu lösen ist
• Besuchermuster : Muster zur Implementierung eines verallgemeinerten Baumdurchlaufs
• Das Besuchermuster trennt
– die speziellen Aktions-Algorithmen
– von den allgemeinen Algorithmen des Durchlaufen eines Baums
• Sein Einsatz ist vor allem dann sinnvoll, wenn ein Baum mit vielen unterschiedlichen
Aktionen durchlaufen wird.
• Hat man nur eine oder zwei Aktionen auf einem Baum auszuführen dann lohnt sich die
zusätzliche Code-Komplexität des Besuchermusters nicht.
Thema des OO-Entwurfs / Softwaretechnik,
siehe entsprechende Veranstaltungen
Seite 27
Binäre Bäume
Baum-Traversierung
Die Knoten eines (binären) Baums können in unterschiedlicher Reihenfolge durchlaufen werden.
Ein etwas komplexeres weil nicht-rekursives Verfahren ist die:
Breiten-Traversierung (Traversierung in level-order / Breadth-First / nicht-rekursiv)
Die Knoten einer Ebene werden komplett durchlaufen bevor die Knoten der nächst-tieferen Ebene
durchlaufen werden.
A
Lege die Wurzel in eine Warteschlange queue;
while (queue ist nicht leer) {
nimm Knoten aus queue;
setze alle Kinder des Knotens in die queue;
bearbeite Knoten;
}
C
B
D
E
Algorithmus zur Breitentraversierung
F
G
Breiten-Traversierung
Seite 28
Binäre Suchbäume
Binärer Suchbaum
Definition: Ein binärer Suchbaum ist
– ein binärer Baum
– bei dem jeder Knoten N einen Wert (den sogen. Schlüsselwert) NK hat
– die Werte sind total geordnet (vergleichbar mit >, <)
– Die Schlüsselwerte sind eindeutig: Zwei unterschiedliche Knoten haben nicht den
gleichen Schlüsselwert.
– für jeden Knoten N mit Nachfolger L und R gilt:
LK < NK < RK
7
5
1
6
3
Seite 29
12
19
15
66
Binäre Suchbäume
Einsatz Binärer Suchbaum
Beobachtung 1:
– In einer völlig geordneten Kollektion (sortierte Liste) können Suchoperationen schnell
ausgeführt werden.
– In einer völlig geordneten Kollektion (sortierte Liste) sind Speicheroperationen aufwändig.
Beobachtung 2:
– In einer völlig ungeordneten Kollektion (unsortierte Liste) sind Suchoperationen aufwändig.
– In einer völlig ungeordneten Kollektion (unsortierte Liste) sind Speicheroperationen
schnell.
Binärer Suchbaum:
– „Halb-geordnete Kollektion“
– sowohl Speicheroperationen als auch Suchoperationen sind einigermaßen effizient.
Seite 30
Binäre Suchbäume
Implementierung binärer Suchbaum
public class SearchTree<T extends Comparable<T>> {
private static class Node<T extends Comparable<T>> {
final T key;
Node<T> left;
Node<T> right;
}
Node(T key) {
this.key = key;
}
private Node<T> root;
private void insert(Node<T> root, T key) {
if (root.key.compareTo(key) == 0) { //already present
return;
} else if (root.key.compareTo(key) < 0) { // key > root
if (root.right == null) {
root.right = new Node<T>(key);
} else {
insert(root.right, key);
}
} else if (root.key.compareTo(key) > 0) { // key < root
if (root.left == null) {
root.left = new Node<T>(key);
} else {
insert(root.left, key);
}
}
}
private void insert(Node<T> root, T key) {
}
public void add(T key) {
if (key == null) { throw new IllegalArgumentException(); }
if (root == null) {
root = new Node<T>(key);
} else {
insert(root, key);
}
}
}
Seite 31
Binäre Suchbäume
Traversieren Binärer Suchbäume
Beobachtung 1:
– Eine Traversierung in In-Order-Reihenfolge liefert alle Elemente in sortierter Reihenfolge.
Beobachtung 2:
– Ein Iterator, der den Baum in In-order-Reihenfolge traversiert, liefert die Elemente in
sortierter Reihenfolge.
Seite 32
Binäre Suchbäume
Traversieren Binärer Suchbäume
Fädelung :
– Der Baum wird mit zusätzlichen Zeigern („Fäden“) ausgestattet
die auf den jeweils nächsten Knoten bei einer Traversierung zeigen.
– Die Fädelung erleichtert das Durchlaufen des Baums.
Start
Stopp
7
5
Binärer Suchbaum mit
in-order-Fädelung.
1
Aufgabe:
6
3
Welche Zeiger der Fädelung könnte man
weglassen?
Seite 33
12
19
15
66
Binäre Suchbäume
Traversieren Binärer Suchbäume
Iterator:
– Mit Fädelung: Fädele Baum, durchlaufe die Fädelung
– Ohne Fädelung:
Iterator basiert auf Baumdurchlauf mit Stack statt Rekursion.
Start
Stopp
7
5
1
12
6
19
Beobachtung:
Die In-Order-Traversierung kann mit einem Stapel
unverarbeiteter Eltern-Knoten organisiert werden:
Solange es ein linkes Kind gibt, staple den Knoten
und gehe nach links. Danach nimm Knotenwert und
gehe nach rechts.
3
Seite 34
15
66
Binäre Suchbäume
Traversieren Binärer Suchbäume
Rekursionsfreier in-Order-Baumdurchlauf
private static <T extends Comparable<T>> void visitInOrderS(Node<T> root, Action<T> a) {
List<Node<T>> stack = new LinkedList<>();
Node<T> k = root;
}
while (!stack.isEmpty() || k != null) {
while (k != null) {
stack.add(0, k);
k = k.left;
}
k = stack.remove(0);
a.act(k.key);
k = k.right;
}
7
5
Rekursionsfreie in-order Baum-Traversierung
1
6
3
Seite 35
12
19
15
66
Binäre Suchbäume
Traversieren Binärer Suchbäume
Traversieren mit Iterator
– Beobachtung:
 hasNext ~ Schleifenkontrolle
 next
~ Schleifenkörper
bei rekursionsfreier Traversierung
HasNext
while (!stack.isEmpty() || k != null) {
while (k != null) {
stack.add(0, k);
k = k.left;
}
k = stack.remove(0);
a.act(k.key);
k = k.right;
next
der nächste Schlüsselwert
}
Seite 36
Binäre Suchbäume
Traversieren Binärer Suchbäume
mit Iterator
public class SearchTree<T extends Comparable<T>> implements Iterable<T> {
. . .
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
List<Node<T>> stack
Node<T>
k
= new LinkedList<>();
= root;
@Override
public boolean hasNext() {
return !stack.isEmpty() || k != null;
}
@Override
public T next() {
while (k != null) {
stack.add(0, k);
k = k.left;
}
k = stack.remove(0);
T res = k.key;
k = k.right;
return res;
}
}
}
};
@Override
public void remove() {
throw new UnsupportedOperationException();
}
Seite 37
Der Baum darf während des
Durchlaufs natürlich nicht
verändert werden!
Ausgeglichene Binäre Suchbäume
Baumstruktur - Bedeutung
– Beobachtung
Ein Suchbaum kann unter Umständen „entarten“: Er hat keine richtige Baumstruktur,
sondern entspricht mehr oder weniger ganz oder in Teilbäumen einer Liste.
Degenerierte Suchbäume haben schlechtere Such- und Einfügeoperationen mit
geringerer Effizienz als Bäume mit ausgeglichener Baumstruktur.
– Schlussfolgerung
Lasse nur Bäume zu, die eine ausgeglichene Struktur haben.
7
1
3
5
5
12
6
7
1
6
19
12
15
3
19
66
Seite 38
15
66
Ausgeglichene Binäre Suchbäume
Definitionen / Begriffe in Zusammenhang mit Bäumen
– Tiefe (Stufe, Ebene) eines Knotens
Die Tiefe eines Knotens ist definiert als die Länge des Pfades (=Anzahl der Knoten) von
der Wurzel zum Knoten.
– Höhe eines Baums
Die Höhe h(T) eines Baums T ist das Maximum der Tiefen all seiner Knoten.
Der leere Baum hat die Höhe -1
Ein Baum mit einem Knoten hat die Höhe 0
– Gewicht eines Baums
Das Gewicht g(T) eines Baums T ist die Anzahl seiner Blätter.
– Entarteter Baum
Ein Baum ist entartet, wenn seine Höhe gleich der Zahl seiner Knoten ist.
Seite 39
Ausgeglichene Binäre Suchbäume
Ausgeglichene Binärbäume
Zwei gängige Kriterien der Ausgeglichenheit (Balanciertheit) definieren die Ausgeglichenheit:
– Höhen-balancierte Bäume
Bei Höhen-balancierten Bäumen ist die Differenz der Höhe der Unterbäume beschränkt.
– Gewichts-balancierte Bäume
Bei Gewichts-balancierten Bäumen ist die Differenz des Gewichts der Unterbäume
beschränkt.
Seite 40
Ausgeglichene Binäre Suchbäume
Höhen-balancierte Binärbäume
k-balancierter Baum
Ein k-balancierter Baum ist ein Baum, bei dem die Differenz der Höhe der beiden Unterbäume maximal
k ist.
Ein k-balancierter Baum T ist entweder
– leer, oder
– er hat zwei Unterbäume TL und TR mit | h(TL) - h(TR) | ≤ k
Mit k kann das Kosten-Nutzen-Verhältnis variiert werden.
Kleines k: sehr ausgewogen, aber eventuell viele Reorganisationen notwendig.
Großes k: langsame Suche, aber weniger Reorganisationen notwendig.
4
2
1
6
3
5
9
8
Ist dieser Baum 2-blanaciert?
10
11
Seite 41
Ausgeglichene Binäre Suchbäume
Höhen-balancierte Binärbäume
k-balancierter Baum
|h(TL ) – h(TR )|
Zeigen Sie, dass dieser
Baum 2-blanaciert ist!
= |max{0, 1} – max {0,1,2,3} |
= |1 - 3|
=2
4
TL
h(TL) = 1
1
0
1
2
0
3
1
2
TR
6
5
TL und TR sind die
Unterbäume (des gleichen
Knotens) mit der größten
Höhendifferenz
9
10
8
11
3
Seite 42
h(TR ) = 3
Ausgeglichene Binäre Suchbäume
AVL-Baum: Höhen-balancierter Binärbaum
Ein 1-balancierter Baum wird AVL-Baum genannt
(von Adelson-Velskii und Landis 1962 publiziert)
Einfüge- und Löschoperationen sind komplex, da die Balance erhalten bleiben muss.
Der Baum muss oft reorganisiert werden.
Seite 43
Ausgeglichene Binäre Suchbäume
Rot-Schwarz-Baum: Fast Höhen-balancierter Binärbaum
Ein Rot-Schwarz-Baum hat geringere Anforderungen an die Ausgeglichenheit als ein AVL-Baum.
– häufig verwendete Methode zur Speicherung von Abbildungen.
– z.B. java.util.Map verwendet Rot-Schwarz-Bäume
Seite 44
Ausgeglichene Binäre Suchbäume
Rot-Schwarz-Baum: Fast Höhen-balancierter Binärbaum
Ausgeglichene (balancierte) Bäume
– sind bei Suchen um so besser, je ausgeglichener sie sind
– allerdings:
 je ausgeglichener sie sein sollen, um so häufiger sind Reorganisationen notwendig
 Feststellen der Ausgeglichenheit ist aufwendig
– Rot-Schwarz-Bäume
 haben weniger Anforderungen an die Ausgeglichenheit:
 Sie haben rote und schwarze Knoten
 Nur die „Schwarzhöhe“ muss ausgeglichen sein.
– Vorteil
 Reorganisation bei Einfügen und Entfernen, wenn die Rot-Schwarz-Eigenschaft
nicht erhalten bleibt
 Suchoperation O(log n) (wie bei anderen balancierten Bäumen)
 Einfüge- und Entferne-Operationen ebenfalls O(log n)
Seite 45
Ausgeglichene Binäre Suchbäume
Rot-Schwarz-Baum: Fast Höhen-balancierter Binärbaum
Rot-Schwarz-Bäume
–
–
–
–
–
–
Alle Knoten sind rot oder schwarz gefärbt
Die Wurzel ist schwarz
Jeder Knoten hat zwei Kinder (Endknoten haben „externe“ Kinder = null)
Jedes Blatt ist schwarz
Jeder rote Knoten hat nur schwarze Kinder
Die „Schwarz-Höhe“ des Baums ist stets ausgeglichen.
Schwarzhöhe
17
10
3
7
26
14
12
30
21
15
16
19
20
23
28
35
41
38
39
auf eine tiefergehende Diskussion wird hier verzichtet.
Seite 46
47
Heap
Heap: Fast Höhen-balancierter, fast sortierter Binärbaum
Heap Verwendung
Datenstruktur die für Prioritätswarteschlangen optimiert ist
– Einfügen eines beliebigen Elements
– Entfernen des Elements mit der höchsten Priorität (des größten / kleinsten)
– gut geeignet für Speicherung in Arrays
Definition Heap
Ein Heap ist ein
– 1-blancierter, links-lastiger, lückenloser Binärbaum
– bei dem der Wert in jedem Knoten nicht kleiner ist als der in seinen Nachfolgern
60
42
30
5
2
4
Beispiel Heap
Dieser „Heap“ hat nichts mit
dem „Heap“-Speicher zu tun.
Seite 47
Heap
Heap: Höhen-balancierter, fast sortierter Binärbaum
Heap als Array
– Ein Heap kann auf Grund seiner Eigenschaften (1-blanciert, linkslastig)
ebenen-weise in einem Array gespeichert werden
60
42
30
5
2
60
4
Seite 48
42
5
30
2
4
Heap
Heap: Höhen-balancierter, fast sortierter Binärbaum
Heap-Operationen
– Einfügen
durch „Hoch-Blubbern“ des neuen Elements bis die Heap-Eigenschaft wieder gilt
– Entfernen
durch „Runter-Blubbern“ („Versickern“) des letzten Elements bis die Heap- Eigenschaft wieder
gilt
Beispiel Hinzufügen:
60
42
30
5
2
4
9 kommt hinzu: Die Heap-Eigenschaft (kein
Nachfolger ist größer) ist verletzt.
9
Welche Arrays
entsprechen den Bäumen?
60
42
30
9
2
4
5
Die 9 wird mit ihrem Vorgänger vertauscht.
Das Verfahren stoppt, da die Heap-Eigenschaft jetzt
erfüllt ist.
Seite 49
Heap
Heap: Höhen-balancierter, fast sortierter Binärbaum
Beispiel Entfernen:
60
42
30
42
60 wird entnommen.
60
30
9
2
4
5
5
9
2
4
Tausch mit dem
größten Nachfolger
5
42
30
9
2
Das letzte Element
wird zur Wurzel.
4
42
5
30
Aufgabe:
Welche Arrays entsprechen den Bäumen?
9
2
4
Tausch mit dem
größten Nachfolger
Seite 50
Heap
Heap: Höhen-balancierter, fast sortierter Binärbaum
Heap: Baum und Array-Darstellung
Die Nachfolger von a[i] sind a[2*i+1] und a[2*i+2]
60
1
3
0
42
30
9
2
60 42 9 30 2 4 5
5
4
0
6
4
v
2
2 3 4
5 6
5
i
2*i + 1
2*i + 2
W
1
i
t
Seite 51
2*i
Heap
Heap-Sort
Sortieren mit einem Heap:
– Schritt 1: Verwandle das vorgegebene Feld in einen Heap
– Schritt 2: Wende die Entnimm-Operation an, bis alles entnommen wurden.
Das Verfahren kann ohne Hilfsfelder auf dem vorgegebenen Feld ausgeführt werden.
Beispiel:
Schritt 1 („heapify“)
2 9 7 6 5 |8
2 9 8 6 |5 7
2 9 8 |6 5 7
2 9 |8 6 5 7
9 |2 8 6 5 7
9 |6 8 2 5 7
|9 6 8 2 5 7
im Baum!
8 ist größer als sein Vorgänger 7: tausche 7 mit seinem größten Nachfolger
5 ist kleiner als sein Vorgänger 9: OK
6 ist kleiner als sein Vorgänger 9: OK
8 ist größer als sein Vorgänger 2: tausche 2 mit seinem größten Nachfolger
2 ist kleiner als seine Nachfolger 6 und 5 : tausche 2 mit seinem größten
Nachfolger
6 ist größer als seine Nachfolger: OK
9 ist größer als seine Nachfolger: OK
Aufgabe:
Welche Bäume entsprechen den Arrays?
Seite 52
Heap
Heap-Sort
Beispiel:
Schritt 2: (entnimm)
9 6 8 2 5 7 9 nach hinten und aus dem Baum entfernen
7 6 8 2 5 |9 Heap-Struktur wiederherstellen
8 6 7 2 5 |9 8 nach hinten und aus dem Baum entfernen
5 6 7 2 |8 9 8 nach hinten und aus dem Baum entfernen
5 6 7 2 |8 9 Heap-Struktur wiederherstellen
7 6 5 2 |8 9 7 nach hinten und aus Baum entfernen
2 6 5 |7 8 9 Heap-Struktur wiederherstellen
6 2 5 |7 8 9 6 nach hinten und aus Baum entfernen
5 2 |6 7 8 9 Heap-Struktur ist gegeben, 5 entnehmen und aus Baum entfernen
2 |5 6 7 8 9 2 entnehmen und aus Baum entfernen
2 5 6 7 8 9 Baum leer: fertig
Welche Bäume entsprechen den Arrays?
http://www.youtube.com/watch?v=a7aXYoFquNY
https://www.youtube.com/watch?v=_bkow6IykGM
Seite 53
Heap
Heap-Operationen
Algorithmus: Entnehmen durch „Versenken des Letzten“
procedure remove : Element = {
assert n > 0
result ← h[1]
h[1] ← h[n]
n ← n-1
bubbleDown(1)
}
procedure bubbleDown(i: NatNum) = {
if 2*i ≤ n
if 2*i+1 > n or h[2*i] ≤ h[2*i+1] then
m ← 2*i
m: Index des
else m ← 2*i+1
Nachfolgers mit der
kleineren Priorität
fi
if h[i] > h[m] then
swap(h[i], h[m])
bubbleDown(m)
fi
fi
}
Achtung: Indexpositionen in h beginnen mit 1.
Seite 54
Heap
Heap-Operationen
Algorithmus: Einfügen durch „Hoch-Blubbern“
procedure insert(e: Element) = {
assert n < w
n++;
h[n] ← e
bubbleUp(n)
}
procedure bubbleUp(i: NatNum) = {
if i=1 or h[i/2] ≤ h[i] then
return
fi
swap(h[i], h[i/2])
bubbleUp(i/2)
}
Achtung: Indexpositionen in h beginnen mit 1.
Seite 55
Herunterladen