Programmierung und Programmiersprachen Sommersemester 2006

Werbung
Programmierung und Programmiersprachen
Sommersemester 2006
Lehrstuhl für Angewandte Telematik / ee-Business
Institut für Informatik
Universität Leipzig
Stiftungslehrstuhl der Deutschen Telekom AG
[email protected]
www.lpz-ebusiness.de
+49 (341) 97 323 30
PuPS
08.05.2006
Übungsgruppen
• Die Übungen zur Vorlesung beginnen in dieser Woche!
•
•
•
•
•
•
•
•
•
# Tag
1 Mo,
2 Mo,
3 Mi,
4 Mi,
5 Do,
6 Do,
7 Fr,
8 Fr,
PuPS
Woche Uhrzeit
Raum
gerade 09:15-10:45 SG 3-07
ungerade 09:15-10:45 SG 3-07
gerade 13:15-14:45 SG 3-97
ungerade 13:15-14:45 SG 3-97
gerade 11:15-12:45 SG 3-01
ungerade 11:15-12:45 SG 3-01
gerade 07:30-09:00 SG 3-11
ungerade 07:30-09:00 SG 3-11
Betreuer
Vincent Wolff-Marting
Vincent Wolff-Marting
Tobias Brückmann
Tobias Brückmann
Ralf Laue
Ralf Laue
Matthias Book
Matthias Book
2
1
Auf dem Weg zu HeapSort
• Problem: Gegeben sei ein Feld a mit n Werten, die sortiert
werden müssen.
• Ausgangssituation: Werte im Feld sind unsortiert
• Ziel: sortiertes Feld
• bereits bekannte Algorithmen (aus DigInf):
• BubbleSort, SelectionSort, InsertionSort, ShellSort,
QuickSort, MergeSort, DistributionSort
• nun betrachtet: HeapSort
• arbeitet auf einem Feld, das einen vollständigen Baum enthält
PuPS
3
Vollständiger Baum
• informale Definition:
• Ein binärer Baum heißt vollständiger Baum, wenn
• mit Ausnahme der untersten Schicht alle Schichten voll besetzt sind
• auf der untersten Schicht von links nach rechts gesehen
bis zu einem bestimmten Punkt alle Knoten existieren
PuPS
4
2
Vollständiger Baum im Feld
• keine Speicherung von Zeigern notwendig - stattdessen:
• verwende die Indices 1…n zur Nummerierung der Knoten
•
•
•
•
Knoten 1 ist die Wurzel, für jeden anderen Knoten i > 1 gilt:
i/2 ist der Vater (mit ganzzahliger Division)
2*i ist der linke Sohn (sofern er existiert)
2*i+1 ist der rechte Sohn (sofern er existiert)
• Feldelement a[i] enthält dann Beschriftung von Knoten i
1
2
3
4
8
5
9
10
6
11
7
12
PuPS
5
Vorbereitung der Implementierung
• Wir werden den HeapSort-Algorithmus im Rahmen der
Klasse Heap implementieren.
• Der Konstruktor für die Klasse Heap allokiert das Feld a.
• Da a[0] vom Algorithmus nicht benutzt wird, benötigen wir ein Feld
mit n+1 Elementen, um n Zahlen zu speichern.
• Die Initialisierung der Feldelemente kann über die Methode
setze erfolgen, das Auslesen über die Methode lese.
• Außerdem brauchen wir später noch die Methode tausche
zum Vertauschen des Inhalts zweier Feldelemente.
PuPS
6
3
Klasse Heap
class Heap {
private int[] a;
public Heap(int n) { a = new int[n+1]; }
public void setze(int i, int x) { a[i] = x; }
public int lese(int i) { return a[i]; }
private void tausche(int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
... // Methoden heapify, baueHeap, sortiere
}
PuPS
7
Heap-Bedingung
• Ein Feld genügt der Heap-Bedingung im Knoten i,
falls im (vollständigen) Unterbaum mit Wurzel i
jeder Knoten eine kleinere Beschriftung als seine Söhne hat
• In Blättern ist die Heap-Bedingung trivialerweise immer erfüllt.
• Im Beispiel ist die Heap-Bedingung in Knoten 3-11 erfüllt,
• nicht jedoch im Knoten 2
• denn a[2] = 12, a[4] = 5 und a[5] = 9,
aber 12 > 5 und 12 > 9
• und auch nicht im Knoten 1
• denn a[1] = 7 und a[3] = 0, aber 7 > 0.
PuPS
8
4
Definition Heap / Konzept HeapSort
• Definition: Das Feld a[1], …, a[n] ist ein Heap,
falls die Heap-Bedingung im Knoten 1 erfüllt ist.
• Hieraus ergibt sich unmittelbar als Konsequenz, dass das kleinste
Element eines Heaps immer in der Wurzel, d.h. in a[1], steht.
• Der Sortieralgorithmus HeapSort arbeitet in zwei Phasen:
• Es ist ein Feld a mit n Elementen gegeben.
• In der ersten Phase wird dieses Feld in einen Heap umgestaltet, d.h.
es wird dafür gesorgt, dass der entsprechende binäre Baum die
Heap-Bedingung im Knoten 1 erfüllt.
• In der zweiten Phase wird aus dem zuvor aufgebauten Heap
systematisch ein geordnetes Feld erzeugt.
PuPS
9
Phase 1: Heap-Aufbau (lokal)
• Idee: Sei in Knoten i die Heap-Bedingung nicht erfüllt
(i ist also kein Blatt), wohl aber in allen Söhnen.
• Dann werden folgende Vertauschungen vorgenommen, um
die Heap-Bedingung in i zu erfüllen:
• falls 2*i <= n, 2*i+1 > n (d.h. i hat nur einen Sohn):
• vertausche a[i] mit a[2*i]
• falls 2*i < n, 2*i+1 <= n (d.h. i hat zwei Söhne):
• suche den Sohn k mit k ∈ {2*i, 2*i+1}, so dass gilt:
a[k] = min {a[2*i], a[2*i+1]}
• vertausche a[i] mit a[k]
• wende diese Idee rekursiv auf k an
PuPS
10
5
Heap-Bedingung in Knoten 2 verletzt
Knoten 1
7
Knoten 2 12
Knoten 4
8
Knoten 3
Knoten 5
5
13
10
9
0
1
3
Knoten 6
Knoten 7
15
Knoten 8 Knoten 9 Knoten 10 Knoten 11
Heap-Bedingung im Teilbaum erfüllt
PuPS
11
Herstellung der Heap-Bedingung
• Die Heap-Bedingung kann im Knoten 2 wie folgt hergestellt
werden:
• Wir vertauschen die Beschriftung im Knoten 2 mit der
Beschriftung desjenigen Sohnes, der die kleinere
Beschriftung trägt. In diesem Fall handelt es sich um den
Knoten 4.
• Wir nehmen hierzu die kleinere der Beschriftungen der
Söhne, damit im Knoten 2 die Heap-Bedingung lokal erfüllt
ist: Die Beschriftung des Knotens 2 ist dann kleiner als die
Beschriftungen beider seiner Söhne.
PuPS
12
6
Herstellung der Heap-Bedingung
• Nun ist zwar die Heap-Bedingung im Knoten 2 lokal
hergestellt. Als Folge ist sie aber im Knoten 4 verletzt!
• Die Vertauschung der Beschriftung eines Knotens mit der
kleineren Beschriftung eines seiner Söhne lässt sich an
dieser Stelle wiederholen, sodass die Beschriftung 12 in den
Knoten 8 (ein Blatt) wandert.
• Trivialerweise ist die Heap-Bedingung in einem Blatt erfüllt.
• Durch das beschriebene Vorgehen haben wir also
schrittweise dafür gesorgt, dass die Heap-Bedingung im
Knoten 2 hergestellt wurde.
PuPS
13
Methode heapify
• Zur lokalen Herstellung der Heap-Bedingung dient die
Methode heapify der Klasse Heap. Sie bekommt als
Eingabe den Knoten dieserKnoten und die aktuelle
heapGröße des Feldes.
• Zunächst werden der linke und der rechte Sohn bestimmt.
Danach findet eine Fallunterscheidung statt:
• Fall 1: Der linke Sohn liegt noch im Feld, der rechte Sohn nicht mehr:
In diesem Fall muss lediglich getestet werden, ob die Beschriftung
dieserKnotens größer ist als die Beschriftung seines einzigen
Sohnes.
• Fall 2: Auch der rechte Sohn liegt noch im Feld: In diesem Fall muss
der Sohn mit der kleineren Beschriftung identifiziert und die
Beschriftung dieserKnotens mit der Beschriftung des kleineren
Sohns verglichen werden.
PuPS
14
7
Methode heapify
• Falls der Vergleich ergibt, dass die Heap-Bedingung verletzt
ist (d.h. der Sohn ist kleiner als dieserKnoten):
• mit Hilfe der elementaren Methode tausche wird der Inhalt
dieserKnotens mit dem seines Sohnes vertauscht
• da nun die Heap-Bedingung im Unterbaum, dessen Wurzel der
Sohnknoten ist, verletzt sein kann, wird heapify rekursiv mit dem
Sohn und der Heapgröße erneut aufgerufen.
PuPS
15
Heap-Bedingung in Knoten 1 verletzt
Knoten 1
Knoten 2
Knoten 4
12
8
5
Knoten 3
Knoten 5
13
7
10
9
0
1
3
Knoten 6
Knoten 7
15
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
16
8
Phase 1: Heap-Aufbau (global)
• Die Heap-Bedingung gilt im Beispiel immer noch nicht für
den Knoten 1, da die Beschriftungen beider Söhne
(Knoten 2 und 3) kleiner sind als die Beschriftung der
Wurzel des Baumes.
• Lösung: Um die Heap-Bedingung im ganzen Baum zu
erfüllen, verwenden wir die Methode baueHeap, die
rückwärtslaufend in jedem Knoten k = n/2, …, 1 die HeapBedingung durch Aufruf von heapify herstellt.
• Knoten k > n/2 müssen wir nicht durchlaufen, da sie Blätter sind
• Rückwärtslaufen nötig, um aus kleineren Heaps größere aufzubauen
• Söhne sind immer schon Wurzeln von kleineren Heaps
• zur lokalen Herstellung der Heap-Bedingung muss immer nur einer
der Unterbäume modifiziert werden
PuPS
17
Phase 2: Sortierung des Felds
• Nachdem heapify durch baueHeap in der beschriebenen
Art aufgerufen worden ist, stellt das Feld a einen Heap dar,
der jetzt in der Methode sortiere dazu verwendet werden
kann, das Feld zu sortieren.
• Wir machen uns zunutze, dass in einem Heap das kleinste
Element immer in der Wurzel liegt. Also nehmen wir es aus
dem Heap heraus und legen es im sortierten Abschnitt des
Felds ab. Dazu vertauschen wir einfach a[1] mit a[n].
• Um das zweitkleinste Element zu bestimmen, sorgen wir
nun dafür, dass die restlichen n-1 Elemente wieder einen
Heap bilden. Dazu nehmen wir die Methode heapify.
• Anschließend können wir die Wurzel des neuen Heaps
und a[n-1] miteinander vertauschen und haben so das
nächste Element des Feldes sortiert.
PuPS
18
9
Phase 2: Sortierung des Felds
• In jedem Schritt schrumpft der Heap also um ein Element.
• In den so freiwerdenden Zellen im Feld legen wir die bereits
sortierten Elemente ab.
• Das Feld ist vollständig sortiert, wenn der im nächsten
Sortierschritt noch zu betrachtende Heap nur noch aus der
Wurzel besteht.
• Im Feld stehen dann alle Elemente in absteigender
Reihenfolge.
PuPS
19
HeapSort, Phase 2: 1. Sortierschritt
Aufgebauter Heap
1
4
Austausch
erster
Sortierschritt
6
8
4
9
12
7
9
Reorganisation
des Heaps (ohne
7
1
bereits sortiert
8
9
12
4
Letzten Knoten)
PuPS
6
8
6
12
7
1
20
10
HeapSort, Phase 2: 2.+3. Sortierschritt
Zweiter Sortierschritt
7
8
9
Reorganisation
des Heaps (ohne
die letzten beiden
Knoten)
6
12
4
1
6
8
7
bereits sortiert
9
Dritter Sortierschritt
8
9
12
4
1
12
7
6
4
1
PuPS
21
Methode sortiere
• Der Aufbau eines ersten Heaps aus einem gegebenen Feld
erfolgt durch den Aufruf der Methode baueHeap.
• Da jedes Blatt des Baumes für sich bereits einen Heap
bildet, beginnen wir in der Mitte des Feldes mit dem Aufruf
von heapify und bauen so immer größere Heaps auf.
• Anschließend führt die Methode sortiere das bereits
vorgestellte Vorgehen mit Vertauschen und Reorganisieren
durch.
PuPS
22
11
Aufruf von sortiere()
Knoten 1
Knoten 2
Knoten 4
5
13
12
Knoten 3
Knoten 5
8
0
9
10
1
7
3
Knoten 6
Knoten 7
15
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
23
Aufruf von heapify(1,10)
• heapify(1,10)
• heapify(3,10)
• heapify(7,10)
Knoten 2
Knoten 4
12
Knoten 1
5
Knoten 3
Knoten 5
8
13
1
10
3
7
15
Knoten 6
Knoten 7
9
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
24
12
Aufruf von heapify(1,9)
• heapify(1,9)
• heapify(3,9)
• heapify(6,9)
Knoten 2
Knoten 4
Knoten 1
5
13
12
Knoten 3
Knoten 5
8
10
7
15
Knoten 6
Knoten 7
9
1
3
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
25
Aufruf von heapify(1,8)
• heapify(1,8)
• heapify(2,8)
• heapify(4,8)
Knoten 2
Knoten 4
12
Knoten 1
5
Knoten 3
Knoten 5
8
3
13
1
7
10
15
Knoten 6
Knoten 7
9
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
26
13
Aufruf von heapify(1,7)
• heapify(1,7)
• heapify(3,7)
• heapify(6,7)
Knoten 2
Knoten 4 12
Knoten 1
8
Knoten 3
Knoten 5
3
5
13
10
15
Knoten 6
Knoten 7
9
1
7
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
27
Aufruf von heapify(1,6)
• heapify(1,6)
• heapify(2,6)
• heapify(5,6)
Knoten 2
Knoten 4 12
5
Knoten 1
8
Knoten 3 10
Knoten 5
3
15
1
13
7
Knoten 6
Knoten 7
9
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
28
14
Aufruf von heapify(1,5)
• heapify(1,5)
• heapify(2,5)
• heapify(4,5)
Knoten 2
Knoten 4 12
Knoten 1
9
Knoten 3 10
Knoten 5
3
5
13
15
1
8
7
Knoten 6
Knoten 7
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
29
Aufruf von heapify(1,4)
• heapify(1,4)
• heapify(3,4)
Knoten 1
15
Knoten 2 12
Knoten 4 13
5
Knoten 3 10
Knoten 5
3
1
9
8
7
Knoten 6
Knoten 7
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
30
15
Aufruf von heapify(1,3)
• heapify(1,3)
• heapify(2,3)
Knoten 1
13
Knoten 2 12
Knoten 4 10
Knoten 5
3
5
Knoten 3 15
9
1
8
7
Knoten 6
Knoten 7
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
31
Aufruf von heapify(1,2)
• heapify(1,2)
• heapify(2,2)
Knoten 1
15
Knoten 2 13
Knoten 4 10
5
Knoten 3 12
Knoten 5
3
1
9
8
7
Knoten 6
Knoten 7
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
32
16
Aufruf von heapify(1,1)
Fertig!
Alle Elemente sind
in absteigender
Reihenfolge sortiert.
Knoten 1
15
Knoten 2 13
Knoten 4 10
5
Knoten 3 12
Knoten 5
3
1
9
8
7
Knoten 6
Knoten 7
0
Knoten 8 Knoten 9 Knoten 10 Knoten 11
PuPS
33
Binäre Suchbäume
• Grundidee: Ein vorgelegtes Element x wird in einer
geordneten Menge gesucht, es wird rekursiv vorgegangen.
• Man beschaffe sich das mittlere Element der geordneten
Menge und vergleiche es mit dem vorgelegten Element x.
Stimmt das mittlere Element mit x überein, so ist man fertig;
ist x kleiner, so wende man diese Idee auf alle Elemente an,
die kleiner als das mittlere Element sind; ist x größer, wende
man die Idee auf alle Elemente an, die größer als das
mittlere Element sind. So kommt man durch fortgesetztes
Halbieren sehr schnell zu der Entscheidung, ob x in der
vorgegebenen Menge liegt oder nicht.
PuPS
34
17
Binäre Suchbäume
Definition:
Sei B ein binärer Baum, dessen Knoten mit ganzen Zahlen
beschriftet sind. B heißt binärer Suchbaum, falls gilt:
• B ist leer oder
• der linke und der rechte Unterbaum von B sind binäre Suchbäume,
• ist w die Beschriftung der Wurzel, so sind alle Elemente im linken
Unterbaum kleiner als w, alle Elemente im rechten Unterbaum
größer als w.
PuPS
35
Binäre Suchbäume - Beispiel
16
10
18
14
9
13
PuPS
24
15
36
18
Binäre Suchbäume
• Der Aufbau eines binären Suchbaums erfolgt durch
wiederholtes Einfügen in einen leeren Baum.
• Die Reihenfolge der Werte, die in einen binären Suchbaum
eingefügt werden, bestimmt die Gestalt des Baumes.
• Eine Menge von Werten kann bei unterschiedlichen
Eingabereihenfolgen zu verschiedenen Repräsentationen
als Baum führen.
PuPS
37
Binäre Suchbäume
• Beispiele:
3
1
2
2
Eingabefolge 1 2 3
1
3
Eingabefolge 3 2 1
3
1
Eingabefolge 3 1 2
PuPS
2
1
2
3
Eingabefolge 2 1 3 oder 2 3 1
38
19
Übungsaufgaben 2.1, 2.2
• In der Vorlesung wurde die Implementierung eines
Binärbaums in der Java-Klasse BinaerBaum vorgestellt.
Implementieren Sie darin noch eine Methode
int anzahlKnoten(), die alle Knoten im Baum zählt.
• Formen Sie den folgenden Binärbaum zu einem Heap um.
Schreiben Sie dazu alle notwendigen Operationen auf und
zeichnen Sie das Endergebnis.
E
1
D
A
2
3
I
G
L
C
4
5
6
7
K
J
F
H
B
8
9
10
11
12
PuPS
39
Übungsaufgaben 2.3, 2.4
• Der in der Vorlesung vorgestellte HeapSort-Algorithmus
sortiert die Feldelemente in absteigender Reihenfolge. Wie
muss der Algorithmus verändert werden, um die
Feldelemente aufsteigend zu sortieren?
• Erläutern Sie, wie eine Wertefolge beschaffen sein muss,
die beim Einfügen in einen leeren binären Suchbaum dazu
führt, dass der Baum zu einer Liste entartet.
PuPS
40
20
Übungsaufgabe 2.5
• In der Vorlesung wurde die Implementierung eines binären
Suchbaums in der Java-Klasse BST vorgestellt. Ergänzen
Sie in der zugehörigen Klasse Knoten eine Methode
void entferne(), die den betreffenden Knoten entfernt,
dabei aber sicherstellt, dass der Gesamtbaum ein binärer
Suchbaum bleibt.
PuPS
41
Organisatorisches
• Ergebnisse online unter www.lpz-ebusiness.de
• Rückgabe und Besprechung in den Übungsgruppen
• Notwendige Voraussetzung für Scheinvergabe:
50% der Maximalpunktzahl aller Übungsblätter zusammen
• zwischen 50% und 65% der Punkten u.U. mündliche Prüfung zwecks
Entscheidung über Scheinvergabe
• PuPS-Übungsschein zur Teilnahme an der Einzelklausur
„PuPS“ oder der Kombi-Klausur „DigInf & PuPS“ erforderlich
(außer für Bachelor-Studenten)
• DigInf-Übungsschein in diesem Semester für keine unserer
Klausuren erforderlich
PuPS
42
21
Herunterladen