20170315 Binäre Bäume, Traversierung

Werbung
Departement Mathematik und Informatik
Algorithmen und Datenstrukturen, FS17
Prof Dr Christian Tschudin
15. März 2017
– Amortisierte Analyse
– Binäre Bäume
– Baum-Traversierung
Grace Hopper
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 2 / 27
Uebersicht 2017-03-15
I
Ergänzung zur Vorlesung vom 2. März:
– n2 , n3 etc sind unabhäng. Komplexitätsklassen
– Felddarstellung dynamischer Listen
I
Drei-Summen-Algorithmus mit O(n2 log n)
I
Amortisierte Analyse
– redimensionierb. Felder und “harmloser” Aufwand
I
Baum-ADT:
– Definition, binärer Baum, Syntax-Baum
– Grace Hopper
– Baum-Traversierung
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 3 / 27
Wiederholung
I
Was bedeutet Θ(. . .) ?
I
Für was steht der Kleene-Stern ?
I
Was ist ein “regulärer Ausdruck”?
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 4 / 27
Wichtige Komplexitätsklassen (Korr.)
Familie
konstant
logarithmisch
linear
linearithmetisch
polynomial
exponentiell
Repräsentant Beispiel-Problem
1
log n
n
n log n
n3
n4
...
2n
Addition
Binary Search
Maximum-Suche
Merge-Sort
naives Drei-Summe
Knapsack
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 5 / 27
Verkette Listen mittels Felder (Wiederh.)
I
Interne, verkette Liste für die freien Slots
– innerhalb eines schon vorhandenen Feldes
I
frei zeigt auf ersten freien Slot
– im Nachfolger-Feld wo es weitergeht
– Wert -1 um Ende der freien Liste zu markieren
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 6 / 27
Das Drei-Summen-Problem (Recap)
Gegeben: Liste von Integer-Werten, ohne Wiederholungen
Gesucht: finde/zähle alle drei Werte deren Summe = 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ThreeSum
// (aus Sedgewick and Wayne)
{
public static int count(int[] a)
{ // Count triples that sum to 0.
int N = a.length;
int cnt = 0;
for (int i = 0; i < N; i++)
for (int j = i+1; j < N; j++)
for (int k = j+1; k < N; k++)
if (a[i] + a[j] + a[k] == 0)
cnt++;
return cnt;
}
public static void main(String[] args)
{
int[] a = In.readInts(args[0]);
StdOut.println(count(a));
}
}
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 7 / 27
Verschnellerung Zwei-Summen-Algor.
Idee für verschnellerten Zwei-Summen-Algorithmus:
– gegeben sei der erste Summand x (fest):
– existiert −x in der Liste?
– Falls Liste sortiert ist: Antwort in O(log n)
– Sortierungskosten (Vorwegnahme): O(n log n)
– Total: O(n log n + n(log n)) = O(n log n)
1
2
3
4
5
6
7
8
9
10
11
public static int count2(int[] a)
{ // Count pairs that sum to 0.
Arrays.sort(a);
int N = a.length;
int cnt = 0;
for (int i = 0; i < N; i++)
int x = a[i];
if (BinarySearch.rank(-x, a) > i)
cnt++;
return cnt;
}
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 8 / 27
Verschnellerung Drei-Summen-Algor.
Idee für verschnellerten Drei-Summen-Algorithmus:
– seien die beiden erste Summanden x und y (fest):
– existiert −(x + y) in der Liste?
– Laufzeit: O(n log n + n2 (log n)) = O(n2 log n)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static int count3(int[] a)
{ // Count triples that sum to 0.
Arrays.sort(a);
int N = a.length;
int cnt = 0;
for (int i = 0; i < N; i++)
int x = a[i];
for (int j = i+1; j < N; j++) {
int y = a[j];
if (BinarySearch.rank(-(x+y), a) > j)
cnt++;
}
return cnt;
Warum ist Vergleich
}
>j richtig?
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 9 / 27
“Amortisierte Analyse”
Vorgehen (in den nächsten Slides)
1. Recap Kellerstapel-Implementierung mittels Feld
2. Kellerstapel mit redimensionierbarem Feld
3. Kosten der Feld-Redimensionierung
4. Amortisierte Analyse
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 10 / 27
Kellerstapel mittels Feld (Repetition)
I
I1
2
3
4
5
6
7
8
9
10
ADT-Kellerstapel mittels Feld implementieren:
S: Speicher (ein Feld), N: maximale Stapel-Länge
t: Index-Wert des “obersten” Elements, startet mit 0
push(e):
pop():
if size() == N:
KELLER_VOLL
else:
t = t + 1
S[t] = e
if isEmpty():
KELLER_LEER
else:
e = S[t]
t = t - 1
// <------
Feste Allokation von N ist ein Problem.
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 11 / 27
Feld umdimensionieren (1/2)
Feld vergrössern falls kein Platz mehr vorhanden ist:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void push(String item)
{ // Add item to top of stack.
t++;
if (t == S.length)
resize(2 * S.length);
S[t] = item;
}
// <-----// <------
private void resize(int newMax)
{ // Move stack of size t < newMax to a new array of size newMax.
Item[] temp = (Item[]) new Object[newMax];
for (int i = 0; i <= t; i++)
temp[i] = S[i];
S = temp;
}
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 12 / 27
Feld umdimensionieren (2/2)
Feld verkleinern falls zuviel freier Platz vorhanden ist:
1
2
3
4
5
6
7
8
public String pop()
{ // Remove item from top of stack.
String item = S[t];
S[t--] = null; // avoid loitering
if (t > 0 && t == S.length/4)
resize(S.length/2);
return item;
}
// <-----// <------
Strategie:
– nur schrumpfen wenn weniger als 1/4 gefüllt ist
– und nur um Hälfte schrumpfen (Reserve belassen)
Hysteresis verhindert “Oszillation”
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 13 / 27
Diskussion “Feld umdimensionieren”
Wie wirkt sich das Vergrössern und Schrumpfen auf die
Komplexitätsklasse aus?
I Bei vorgegebener Feldgrösse galt bisher:
push() und pull() sind O(1)
I Bei variabler Feldgrösse gilt neu:
– push(): schlimmstenfalls N Elemente umkopieren
– pull(): schlimmstenfalls N/4 Elemente umkopieren
→ O(n)
I Frage, wie oft dies vorkommt. Selten? Dann harmlos.
Amortisierte Analyse: Vergleiche durchschnittlichen
Aufwand pro Methodenaufruf über Gesamtlaufzeit
Gesamtkosten (inkl Extraaufwand)
Anzahl Methodenaufrufe
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 14 / 27
Amortisierte Analyse (1/2, Beispiel)
Leseanleitung für M Methodenaufrufe, M=65, dann M=128:
Sei Feld schon mit 64 Elementen gefüllt. Das 65ste push() löst
eine Verdopplung aus, nötig werden je 1 Lese- und
Schreibzugriff um jedes der 64 Elemente umzukopieren →
128 Speicherzugriffe. Alle 63 weiteren push() benötigen
keinen Extraaufwand. Verteile die 128 (+64, +32, +16 . . . )
Extrakosten auf die 128 push()-Aufruf: → Durchschnitt ist 5*M.
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 15 / 27
Amortisierte Analyse (2/2, Diskussion)
Ohne Feld-Redimensionierung hängt Laufzeit nicht
von N ab: 1 Speicherzugriff pro push()
I
I
I
I
Mit der Feld-Redimensionierungstechnik:
– einzelnes push() kann O(n) Aufwand auslösen
Amortisierte Analyse, bei M konsekutiven push():
– durchschnittlicher Aufwand ist 5 M pro push()
– d.h. konstante Laufzeitkomplexität, O(1)
M.a.W: Laufzeit hängt weiterhin nicht von N ab,
trotz Feld-Redimensionierung-Technik.
(Aber Aufwand pro push() nun 5 mal höher.)
Algorithmen und Datenstrukturen, FS17
Binärer Baum – Rekursive Definition
Ein binärer Baum ist
– entweder ein Blatt, oder
– ein interner Knoten mit 2 Kindern,
– die selber binäre Bäume sind.
15. März 2017 — 16 / 27
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 17 / 27
Baum – Begriffe
Knote
Kante
Blatt
Wurzel
Eltern
Kind
Geschwister
Knotentiefe
node
edge
leaf
root
parent
child
sibling
depth
Knotenhöhe
height
Baumhöhe
Ebene
height
level
Pfadlänge zur
Wurzel
längster Pfad
zu einem Blatt
= Wurzelhöhe
= Tiefe, auch
= Tiefe + 1
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 18 / 27
Baum: Spezialfall eines → Graphen
I
I
I
I
I
“Baum” wurde von 1857 von A. Cayley eingeführt.
Baum = ungerichteter Graph wobei beliebige zwei
Knoten durch genau 1 Pfad verbunden sind.
Jeder azyklische Graph ist ein Baum.
Jeder der N Knoten eines Baumes kann als Wurzel
dienen (ohne Wurzel: “freier Baum”)
Definition “k-ary tree” für Bäume mit Wurzel:
Jeder innere Knoten hat höchstens k Kinder
(k=2: binärer Baum, k=3: ternärer Baum)
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 19 / 27
Binärer Baum – Eigenschaften
I
I
I
Anzahl Blätter = Anzahl interne Knoten + 1
Anzahl Knoten auf Ebene i ist ≤ 2i
(Wurzel ist auf Ebene 0)
Anzahl der Blätter ist ≤ 2h
wobei h die Höhe des Baums ist.
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 20 / 27
Binärer Baum – Drei Typen
I
I
I
Voller binärer Baum
= jeder Knoten hat 0 oder 2 Kinder
= normale Definition eines binären Baumes
Kompletter binärer Baum
= alle Ebenen sind vollständig gefüllt ausser evtl.
die letzte Ebene wobei “Blätter nur rechts fehlen”
Perfekter binärer Baum =
= alle internen Knoten haben genau 2 Kinder
und alle Blätter sind auf der gleichen Ebene
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 21 / 27
Beispiel Bin. Baum – Arithm. Ausdrücke
I
I
Arithmetischer Ausdruck zum obigen Baum:
(1 - 3) × 5 + (2 + 3) × (4 - 6)
Formel = “Oberflächenstruktur”, Repräsentation
Baum = “Tiefenstruktur”
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 22 / 27
Grace Hopper 1906–1992 (1/2)
I
I
I
Programmierung von “Mark I” (1944) bei der
US-Marine
danach Harvard, Eckert-Mauchly Computer
Corporation, Sperry Rand Corporation (Univac)
“Debugging” wird ihr zugeschrieben. Protokollierte
1947 eine Motte, die in ein Relay von “Mark II” geriet
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 23 / 27
Grace Hopper (2/2)
Hat wichtige Beiträge für die Computer Science geliefert
I
I
I
Erkannte Bezug von arithm Ausdrücken und
Baum-Struktur
Konzept und erste Implementierung von Compilern
– automatische Code-Generierung → A-2 (1954)
– in einer Zeit, wo Computer nur “rechnen” konnten
Heutiger Jargon: eher Linker als Compiler
Plädierte für Programme in menschen-naher Sprache
– in einer Zeit, wo es nur Assembler-Code gab
– Add One To Total statt Total += 1 (→ COBOL)
– wichtiger Einfluss auf COBOL, auch Beiträge
– zu FORTRAN (FORmula TRANslator)
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 24 / 27
Baum-Traversierung (1/4)
I
I
Breitenansatz, breadth-first search, BSF
eine Ebene nach der anderen, von links nach rechts
Tiefenansatz, depth-first search, DFS
zuerst in die Tiefe, dann links nach rechts
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 25 / 27
Baum-Traversierung (2/4)
Welches ist der richtige Ansatz, um arithmetische
Ausdrücke auszuwerten?
I
I
I
“Wert an der Wurzel” kann erst berechnet werden,
wenn die Werte der Unterbäume bekannt sind
Auswertung muss von unten beginnen
Wert ausrechnen, nachdem beide Unterbäume
besucht wurden
→ DFS mit sog. “post-order”-Auswertung
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 26 / 27
Traversierung (3/4): Drei DFS-Strategien
1. Präorder-Strategie
Knoten vor weiterem DFS bearbeiten
2. Inorder-Strategie
Knoten zwischen DFS von Unterbäumen bearbeiten
3. Postorder-Strategie
Knoten nach dem DFS aller Unterbäume bearbeiten
1
2
3
4
def preorder(node n):
process(n)
for child in n:
preorder(child)
Hierarch. Dokument
von A bis Z lesen
1
2
3
4
5
def inorder(node n):
inorder(n[0])
for child in n[1:]:
process(n)
inorder(child)
Arithm. Ausdrücke
“infix” ausdrucken
1
2
3
4
def postorder(node n):
for child in n:
postorder(child)
process(n)
Arithm. Ausdrücke
auswerten
Algorithmen und Datenstrukturen, FS17
15. März 2017 — 27 / 27
Traversierung (4/4): Prefix, Infix, Postfix
DFS-Strategie und Ausgabe der Bauminformation führt zu
verschiedenen Repräsentationen des Syntax-Baums.
Beispiel für arithmetische Ausdrücke:
I
Prefix: → LISP
(+ (+ a (* b c)) (* (+ (* d e) f) g))
I
Infix: → TI-Taschenrechner
a + b * c + (d * e + f) * g
I
Postfix: → HP-Taschenrechner
a b c * + d e * f + g * +
= RPN (reverse polnish notation),
braucht keine Klammern
Herunterladen