2. Höhere Datenstrukturen

Werbung
2. "Höhere" Datenstrukturen
Zur Wiederholung seien hier zunächst die elementaren und aus diesen zusammengesetzte Datenstrukturen
erwähnt
2.1 Elementare Datenstrukturen
2.1.1 Boolean (Typbezeichnung boolean oder kurz bool)
Wertemenge: IB = {TRUE, FALSE} .
Funktionen: Eine mindestens funktional vollständige Menge boolescher Funktionen, z.B. die folgende:
not: IB → IB,
∧, ∨, ⊕ : IB × IB → IB
Die Semantik der Funktionen ist hier durch Wertetabellen der Aussagenlogik festgelegt.
2.1.2 Character (vorgegebener Zeichensatz, z.B. A = {a, b, c, …, z, 0, 1, …, ?, |, &, …})
Die Wertemenge wird als geordnet definiert.
succ: A → A
partielle Funktion
pred: A → A
partielle Funktion
Gleichheit = : A × A → IB
Funktionen:
2.1.3 Integer
Funktionen: +, −, *, /, div, mod, =
2.1.4 Real
M
Wertemenge: = endliche Einschränkung der Menge der reellen Zahlen
Funktionen: +, −, *, / ; spezielle Funktionen exp, sin, cos, …; <, =
Funktionen in weichen im allgemeinen von den Funktionen in ab. Z. B.: wenn x, y ∈ und x + y ∉ ,
dann Ersatz durch eine Approximation (Rundung).
M
M
M
Die bekannten Axiome sind daher i. a. falsch!
2.1.5 Aufzählungstyp (enumeration type)
Wertemenge:
Endliche geordnete Menge (x1, …, xn) von Symbolen, xi ∈ A *, A ein Alphabet (Character)
Funktionen:
succ, pred (partiell definiert); =, <
2.1.6 Unterbereichstypen
Benutzerdefinierte Typen sind oft Einschränkungen von Standardtypen (subtype).
Beispiele:
type eins_bis_hundert = l..100
Werktag = (Mo..Fr)
als Unterbereichstyp von Integer;
als Unterbereichstyp von Wochentag.
2.1.7 Zeiger (Pointer)
Einem Datentyp wird mittels ref (Bezeichnung nach ALGOL 68) ein Zeiger zugeordnet. Ein Datum vom Typ
ref t ist eine Speicheradresse zu einem Datum vom Typ t. Man unterscheidet:
•
Typ-unabhängige Zeiger, maschinennah, jede Adresse zuweisbar, Typ: pointer
•
Typ-abhängige Zeiger, ref t zu jedem t.
Für die Festlegung von t unterscheidet man zwei Fälle:
•
t beliebig: ermöglicht z.B. Typen real, ref real, ref ref real, ..., ref n, n > 0
(z.B. in ALGOL 68; schwer zu programmieren)
•
t einschränken auf nicht-ref-Typen.
2.2 Zusammengesetzte Datenstrukturen
2.2.1 String (Zeichenketten über einem Alphabet A)
String of char, String of boolean, ...
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-1
Operationen zur Konstruktion
ε() : string
ι ([char]) : string
°([string], [string]) : string
: erzeugt einen leeren String
: erzeugt einen String aus einem Zeichen
: Konkatenation von Strings
Zugriffsoperationen
access ([string], [nat]) : char : Zugriff auf ein einzelnes Zeichen
length ([string]) : nat
: Zugriff auf Länge des Strings
Test Operationen
= ([string], [string]) : bool
< ([string], [string]) : bool
2.2.2 Felder (arrays)
Ein- oder mehrdimensionale Speicherung gleichartiger Daten und Zugriff über Indizes.
1-dimensionale Felder:
Typbezeichnung: array
Typ der Indizes:
index
Typ der Elemente: elem
Operationen zur Konstruktion:
create(): array
(erzeugt ein Feld bei vorgegebener Indexmenge, aber noch
ohne Einträge)
assign([array], [index], [elem]): array
(Eintrag eines Elementes)
Zugriffsoperation:
access([array], [index]): elem;
Übliche Schreibweisen:
(Zugriff auf ein Element)
a[i]: = e
statt a := assign(a,i,e)
a[i]
statt access(a,i)
Mehrdimensionale Felder:
Indexmenge ist ein Cartesisches Produkt mehrerer Komponenten, z.B. I = I1 × I2 × I3
Beispiel:
array[1..100,0..30,-20..20] of elem
Dies kann identifiziert werden mit
array[1..100] of array[0..30] of array[-20..20] of elem
2.2.3 Verbund (record)
Gegeben seien k Typen m1, …, mk und k Namen für Selektoren S1,. .., Sk . Die daraus gebildete Struktur eines
Verbundes wird mit
record(S1: m1; …; Sk: mk)
bezeichnet. Jeder konkrete Verbund enthält (bis zu) k Komponenten der Typen m1, …, mk , auf welche mit den
Selektoren S1, ..., Sk zugegriffen wird.
Programmiersprachliche Notation (PASCAL):
r: record
S1: m1;
...
Sk: mk;
end;
Zugriff auf die i-te Komponente: r.Si
Mathematisch betrachtet entspricht der Verbund einem Cartesischen Produkt.
2.2.4 Speicherstrukturen Stack, Queue, Deque
Diese Speicherstrukuren dienen zum Abspeichern und Lesen von Elementen gleichen Typs mit speziellen
Zugriffsbedingungen.
LIFO (Last-In-First-Out)-Prinzip (Stack oder Kellerspeicher)
FIFO (First-In-First-Out)-Prinzip: charakterisiert die Queue oder Warteschlange
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-2
LIFO/FIFO-Kombination double-ended-queue oder kurz Deque :und kann wahlweise wie ein Stack oder eine
Queue benutzt werden.
Für jede dieser Strukturen führen wir eine Operation create ohne Parameter ein, welche eine leere Struktur der
jeweiligen Art erzeugt, sowie eine Testfunktion empty.
Für den Datentyp stack, dessen Elemente von einem Typ elem sind, haben wir die folgenden Operationen:
create(): stack
push ([elem], [stack]): stack
pop ([stack]): stack
Beachte: pop liefert hier nicht das oberste Stackelement, sondern den um 1
Element reduzierten Stack!
top ([stack]): elem
empty ([stack]): bool
full([stack]): bool
Hierzu gehören die folgenden Axiome:
1. top(push(e, s)) = e
2. pop(push(e, s)) = s
3. empty (create) = TRUE
4. empty(push(e, s)) = FALSE.
Für die Datenstrukturen queue und deque wird entsprechend verfahren.
2.2.5 Listen
Listen sind eigentlich eine Verallgemeinerung von Strings, wobei statt char ein allgemeiner Typ elem zugelassen
wird. Wir formatieren die Listen aber so, dass auch die Speicherstrukturen Stack, Queue und Deque darin
sichtbar werden:
create( ): list
appendr([list], [elem]): list
appendl([list], [elem]): list
concat([list], [list]): list
headr([list]): elem
headl([list]): elem
tailr([list]): list
taill([list]): list
length([list]): nat
access([list], [nat]): elem
empty([list]): bool
equal([list], [list]): bool
Die Semantik wird direkt mit Hilfe der Repräsentation der Listen durch Tupel erklärt: ⟨e1, ..., en⟩, n ≥ 0 , mit
Elementen ei vom Typ elem:
create = ()
appendr(⟨e1, ..., en⟩, e) = ⟨e1, ..., en, e⟩
appendl(⟨e1, ..., en⟩, e) = ⟨e, e1, ..., en⟩
concat(⟨e1, ..., en⟩, ⟨e1', ..., em' ⟩) = ⟨e1, ..., en, e1', ..., em' ⟩
headr(⟨e1, ..., en⟩) = en
headl(⟨e1, ..., en⟩) = e1
tailr(⟨e1, ..., en⟩) = ⟨e1, ..., en−1⟩
taill(⟨e1, ..., en⟩) = ⟨e2, ..., en⟩
access(⟨e1, ..., en⟩), i) = ei
(l < i < n)
length(⟨e1, ..., en⟩) = n
empty(⟨e1, ..., en⟩) = (n = 0 TRUE, FALSE)
equal(⟨e1, ..., en⟩, ⟨e1', ..., em' ⟩) = ((n = m ∧ ei = e'i für alle i ∈ {1, ..., n}) TRUE, FALSE)
2.2.6 Mengen
Der Datentyp set of elem wird benutzt zur Beschreibung von Mengen {e1, ..., en} mit e1, ..., en ∈ E, wobei E
eine Menge von Elementen vom Typ elem ist. Darauf sind folgende Operationen definiert:
create( ): set
append([set], [elem]): set
∩ ([set], [set]): set
∪([set], [set]): set
card([set]): nat
∈ : ([elem], [set]): bool
⊆ : ([set], [set]): bool
= : ([set], [set]): bool
Die Semantik ergibt sich aus den bekannten mengentheoretischen Operationen.
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-3
2.3 Dateien
In vielen Anwendungen, insbesondere bei Datenbanken, sind große Mengen von Sätzen (Records) gleicher
Struktur im Sekundärspeicher (z.B. Festplatte) zu speichern.
Datei (File) = Mengen von Sätzen gleicher Struktur. Typische Operationen auf Dateien sind:
•
Einfügen eines Satzes
•
Löschen eines Satzes
•
Modifizieren eines Satzes
•
Auffinden eines Satzes mit einem bestimmten Wert in einem bestimmten Feld, oder einer bestimmten
Wertekombination auf einer bestimmten Menge von Recordfeldern. ("lookup Operation")
Der Zugriff auf die Sätze einer Datei erfolgt häufig über einen Schlüssel. Dieser muss die Sätze eindeutig
identifizieren.
2.3.1 Sequentielle Files
Sätze werden hintereinander (sequentiell) angeordnet.
Beispiel (Pascal):
type person = record p-nummer: longint; ... – p-nummer ist Schlüssel
var personendatei : file of person;
Es seien nun n Datensätze in beliebiger Reihenfolge gespeichert.
− Aufsuchen des i-ten Satzes: mit seek-Funktion (1 Zugriffsoperation)
− Aufsuchen eines Satzes mit gegebenem Schlüssel (z.B. p-nummer = 101) erfordert im Mittel n/2
Satzzugriffe.
− Löschoperationen sind ähnlich aufwendig. Lücken nach Löschoperationen
− Einfügen eines neuen Satzes am File-Ende: konstanter Zeitaufwand.
Wenn die Datensätze z.B. nach aufsteigenden Schlüsselwerten in der Datei eingetragen sind:
− Aufsuchen der Einfügestelle (≈ log n Schritte)
− Einfügen: verschieben aller nachfolgenden Sätze (im Mittel n/2 Zugriffe)
− Löschoperationen sind ähnlich aufwendig. Lücken nach Löschoperationen
2
− Ausgabe nach Schlüsseln geordnet: sehr aufwendig O(n )
− Ausgabe nach Schlüsseln geordnet: einfach O(n)
2.3.2 Organisation durch lineare Listen
Sätze werden mit Zeigern verkettet.
- ungeordnetes Eintragen der Sätze:
aufwendiges Suchen O(n)
Einfügen neuer Sätze: einfach O(1)
Löschoperationen: einfach (jedoch zuerst Suchoperation erforderlich) O(1)
2
Ausgabe nach Schlüsseln geordnet: sehr aufwendig O(n )
- Eintragen nach Schlüsseln geordnet:
aufwendiges Suchen O(n)
Einfügen neuer Sätze: aufwendig O(n)
Löschoperationen einfach (jedoch zuerst Suchoperation erforderlich) O(1)
Ausgabe nach Schlüsseln geordnet: einfach O(n)
2.3.3 Indexsequentieller Zugriff
Die Datei ist in Blöcken organisiert. Jeder Record besitzt einen eindeutigen Schlüssel. Jeder Block kann mehrere
Records aufnehmen. Blöcke sind nicht notwendig voll.
Zusätzlich gibt es einen Indexfile (sequentiell organisiert). Im Indexfile sind Record-Schlüssel zusammen mit
Verweisen auf Blocknummern eingetragen.
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-4
Aachen
Aachen
Augsburg
Info zu Aachen
Info zu Augsburg
Berlin
Bochum
Clausthal
Info zu Berlin
Info zu Bremen
Info zu Clausthal
Block 1
Berlin
Darmstadt
Zittau
Darmstadt Info zu Darmstadt
Dortmund Info zu Dortmund
Block 2
Block 3
nur Schlüssel
(alphabetisch geordnet)
Schlüssel
weitere Information
Operationen:
− Suchen:
1. auf Indexfile (binär, log m bei m Einträgen)
2. im Block (linear durchsuchen)
− Löschen: Eintrag im Block löschen.
Wenn der erste Eintrag im Block gelöscht wird: Eintrag im Indexfile ändern
Wenn der Block leer ist, dann kann er „freigegeben" werden. Eintrag im Indexfile löschen
− Einfügen: Im Block, sofern noch Platz ist. Andernfalls neuen Block anlegen.
Schlüssel im Indexfile an der richtigen Stelle eintragen.
Im folgenden drei Datenstrukturen (Hashing, Bäume, B-Bäume) haben ein besseres zeitliches Verhalten bei den
Standardoperationen zum Ziel.
2.3.4 Hashing (gestreute Speicherung)
Prinzip: Hash-Tabelle steht für die Aufnahme von m ∈ IN Sätzen zur Verfügung. Die Position, in der der Satz
gespeichert wird, wird aus dem Schlüssel berechnet; Hash-Funktion
Sei S die Menge aller möglichen Schlüsselwerte;
Sei h: S → {0, ..., m − 1) eine Hash-Funktion
Vorgehen:
der erste Satz (Schlüssel s1) wird in Position h(s1) eingetragen
der zweite in h(s2), usw.
Dabei können Kollisionen auftreten:
Schlüssel si und sj mit si ≠ sj und h(si) = h(sj).
Mögliche Verfahren im Kollisionsfall:
(1) re-hashing: statt einer Hash-Funktion werden m verschiedene Hash-Funktionen gewählt
Bei Kollision wird die nächste Hash-Funktion genommen
Beispiel: gegeben h0 = h : S → {0, ..., m − 1}
hi(s) := (h(s) + i) mod m, i = 1, ..., m − 1
(2) Überlaufliste: Bei Kollision wird der neue Satz in eine Liste eingetragen
(3) m Überlauflisten: jede Zeile der Hash-Tabelle besitzt einen Pointer zu einer eigenen
Verhalten beim Aufsuchen und Löschen: von der Kollisionsbehandlung abhängig
- Re-hashing: maximal O(m) Zugriffe auf die Hash-Tabelle erforderlich
- Überlauflisten: max. n Schritte
Forderungen an Hash-Funktionen: sollen möglichst gut "streuen" ("gestreute Speicherung"), damit eine
gleichmäßige Belegung der Hash-Tabelle mit möglichst wenig Kollisionen erreicht wird
Bei guter Streuung sehr schnell (nur 1 Tabellen-Zugriff)
Nachteil: Sortiertes Ausgeben der Sätze nicht möglich
2.3.5 Binäre Bäume
Jeder Knoten nimmt l Record auf und besitzt außerdem zwei Pointer (auf den linken und den rechten
Nachfolgeknoten).
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-5
Beispiel:
Bochum
Augsburg
Aachen
Clausthal nil
Berlin
nil nil
nil nil
Dortmund
nil
Darmstadt nil nil
Sortiertes Einfügen erfolgt "In-Order": links die kleineren, rechts die größeren Schlüssel.
Suchen eines Satzes mit gegebenem Schlüssel: Zeitaufwand ist proportional zur Höhe des Baumes.
Löschen eines Satzes: Zuerst aufsuchen des Satzes. Zeitaufwand für das Löschen selbst ist konstant.
Einfügen eines Satzes: Zuerst Aufsuchen eines Blattes, an das der neue Satz angehängt wird. Einfügen selbst
braucht konstanten Zeitaufwand.
Wenn ein binärer Baum n Knoten hat, dann gilt für dessen Höhe h: log2 n ≤ h ≤ n
Wenn h ≈ log2 n dann haben alle Blätter ungefähr die gleiche Tiefe ("ausgeglichener Baum"). Wenn h ≈ n, dann
liegt ein "entarteter Baum" vor. Ob ein Baum entartet ist oder nicht, hängt von der Reihenfolge ab, in der die
Sätze eingetragen werden.
Definition (ausgeglichener Baum). Sei im Knoten i der Zeiger zum linken Nachfolger mit NL(i) und zum
rechten Nachfolger mit NR(i) bezeichnet. Die linke und rechte Tiefe des Knotens i ist rekursiv definiert als
 0 falls NL(i) = nil
 0 falls NR(i) = nil
TL(i) := 
TR(i) := 
 max{TL(j), TR(j)} + 1 sonst
 max{TL(k), TR(k)} + 1 sonst
Dabei bedeuten j und k den linken und rechten Nachfolgerknoten von i (falls vorhanden). Die Balance von i ist
definiert als B(i) := abs(TR(i) − TL(i)). Der Baum heißt ausgeglichen, falls B(i) ≤ 1 gilt.
Adelson-Algorithmus: reorganisiert den Baum, sobald er nicht ausgeglichen ist.
Beispiel: TL(c) = 1, TR(c) = 3 ⇒ B(c) = 2;
c
d
Reorganisation
a
m
m
c
d
r
a
f
r
f
Nach jedem Einfüge- bzw. Löschschritt wird ggf. reorganisiert.
2.3.6 Geordnete Bäume einer festen Ordnung k
Jeder Knoten hat m ≤ k Nachfolger
Jedem Nachfolger ist eindeutig ein Index aus {1, ..., k} zugeordnet (injektiv)
1., 2., ..., k-ter Nachfolger; dabei können auch einige fehlen
Jeder Knoten
- nimmt bis zu k − 1 Records auf, die nach Schlüssel geordnet sind
− hat bis zu k Pointer, die zu Nachfolger Knoten zeigen.
S1
S1
S2
...
Sk-1
...
...
n
Höhe des h Baumes bei n Knoten: logk(k−1) ≤ h ≤ n
n
h = logk(k−1)
... ausgeglichener Baum; alle Blätter ungefähr gleiche Tiefe
h ≈ n ... entarteter Baum
Operationen
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-6
Suchen: Zeitaufwand O(h) (linear in h)
Einfügen: O(h)
Löschen: O(h)
Ausgabe nach Schlüsseln geordnet: O(n)
2.3.7 B+-Bäume
B ... steht für "Bayer" (nicht für "binär!)
B+-Bäume erlauben die Definition von Index-Hierarchien. Seien d ≥ 2 und e ≥ 1 natürliche Zahlen
Eigenschaften:
jeder Weg von der Wurzel zu einem Blatt hat dieselbe Länge
B+-Bäume können wachsen oder schrumpfen
jeder Knoten ("Block") enthält Platz für 2d − 2 Schlüssel und 2d − 1 ≥ 3 Pointer zu Nachfolgeknoten.
die Blockgrösse ist so gewählt, dass mit nur einem Plattenzugriff ein ganzer Block gelesen bzw. geschrieben
werden kann
in jedem Knoten (ausser dem Wurzelknoten) sind mindestens d Pointer eingetragen
die vollständigen Sätze (nicht nur deren Schlüssel) sind in den Blatt-Knoten gespeichert
(in jedem Blatt-Knoten: e ≤ Anzahl_gespeicherte_Schlüssel ≤ 2e − 1)
jeder Knoten (mit Ausnahme der Blätter) enthält genau 1 Pointer mehr als Schlüssel
Die Menge der Blatt-Knoten stellen den "Hauptfile" dar: dort sind die vollständigen Sätze eingetragen.
Die im Baum darüberliegenden Blöcke beschreiben die "Indexstruktur"
Beispiel: B+-Baum mit d = 2, e = 2
B2
1
4
B5
9
16
9
B6
25
36
B1
25
144
B3
64
100
49
B7
64
B4
81
100
B8
B9
121
196
144
169
B10
196
225
256
B11
Für einen B+-Baum mit n Records (Schlüssel) gilt:
Anzahl der Blätter: ≤ n/e
Weglänge 1 (Wurzelknoten):
≥ Pointer
Weglänge 2:
≥ 2d Pointer
...
≥ 2d l−1 Pointer
Weglänge l:
n
Für die Anzahl der Blätter gilt daher: 2d l−1 ≤ Anzahl Blätter ≤ e ; d.h. ein Baum der Höhe l enthält mindestens
n
n ≥ 2ed l−1 Records. Bei n im Hauptfile gespeicherten Records sind somit höchstens l ≤ 1 + logd(2e)
Blockzugriffe erforderlich.
Beispiel: n = 106, e = 5, d = 50 ⇒ l = 1 + log50 105 ≈ 6
Zeitaufwand für Operationen:
n
Aufsuchen eines Satzes: 1 + logd(2e) Blockzugriffe
Einfügen:
° Wenn der betreffende Block B im Hauptfile weniger als 2e − 1 Records enthält:
den neuen Satz eintragen.
Andernfalls: neuen Block B' erzeugen, und die Sätze von B zusammen mit dem neuen Satz auf B und B'
gleichmässig verteilen (jeder bekommt dann e Sätze). Im Vaterblock von B Zeiger auf B' und Schlüssel
des ersten Records von B' eintragen.
° Wenn Vaterblock voll ist (d.h. der Eintrag ist nicht möglich), dann Vaterblock splitten
ähnlich wie vorhin: B in B und B' splitten. Im Vaterblock der Vaterblocks müssen dann ein Schlüssel und
ein Pointer eingetragen werden.
° Das Splitten kann sich bis zur Wurzel (rekursiv) fortsetzen (siehe Beispiel).
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-7
Löschen:
Im Prinzip invers zur Einfügeoperation.
° Wenn ein Block B im Hauptfile nach dem Löschen e − 1 Records hat, dann einen Geschwisterblock B'
aufsuchen und die Sätze in einem Block zusammenlegen (falls möglich), andernfalls gleichmässig auf B
und B' aufteilen.
Zeitaufwand beim Einfügen und Löschen: Da sich die Struktur des Baumes längs eines Pfades vom Blatt zur
n
Wurzel ändern kann, ist der Zeitaufwand proportional zur Weglänge, also zu 1 + logd(2e).
Beispiel: Einfügen eines Schlüssels 32 in obigen B+-Baum, und anschliessendes Löschen von Satz mit Schlüssel 64.
− Einfügen von Schlüssel 32:
64
25
144
36
9
1
9
4
25
16
32
36
100
49
64
196
81
100
144
121
169
196
225
256
− Löschen von Schlüssel 64:
25
9
1
4
9
81
36
16
25
32
36
144
49
81
100
121
144
196
169
196
225
256
Eigenschaften (seien d und e natürliche Zahlen, d ≥ 2, und e ≥ 1)
- innere Knoten speichern nur Schlüssel und Pointer zu Nachfolgeknoten
2d − 2 Schlüssel, 2d − 1 Pointer
- in jedem inneren Knoten sind mindestens d pointer eingetragen (außer im Wurzelknoten)
- Blattknoten enthalten die vollständigen Sätze
- in jedem Blattknoten sind mindestens e Sätze eingetragen
- Höhe des Baumes kann sich beim Einfügen/Löschen verändern
- die Knotengröße (bestimmt durch e bzw. d) sollte so gewählt sein,
dass bei jedem Plattenzugriff ein kompletter Knoten in den Speicher geholt wird
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-8
Löschen und Einfügen im Detail:
neuen Schlüssel
32 einfügen
B1
B2
25
144
64
100
B3
9
B4
196
36
1
16
9
4
B5
25
B6
36
49
64
B7
Knoten
splitten
25
81
100
B8
32
36
121
144
B9
196
169
B10
225
256
225
256
B11
49
B1
25
144
64
B2
Knoten
splitten
1
36
64
B6
81
100
B8
25
32
36
196
100
16
9
4
B5
B4
100
64
9
121
144
B9
196
169
B10
B11
49
64
Knoten splitten
neuen Wurzelknoten einführen
B2
1
9
144
25
144
36
100
16
9
4
B5
25
64
B6
81
100
B8
25
32
36
B4
121
196
144
B9
169
B10
196
225
256
B11
49
64
resultierender Baum:
B2
1
4
B5
9
9
144
36
100
16
64
B6
B8
25
© 2002 K. Ecker
25
32
36
81
B4
100
B9
121
196
144
169
B10
196
225
256
B11
49
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-9
64
Schlüssel 64
löschen
B2
1
9
144
36
100
16
9
4
B5
25
64
B6
B4
81
100
B8
25
32
36
196
121
144
B9
196
169
B10
225
256
225
256
B11
49
64
B2
1
9
144
36
100
16
9
4
B5
25
64
100
81
B6
81
25
32
36
B4
100
49
196
121
144
196
169
B10
121
B11
Knoten
kominieren
64
25
B2
144
B4
100
36
9
196
144
1
16
9
4
B5
25
32
36
49
81
100
Knoten
kominieren
196
144
121
B6
169
B10
196
225
256
225
256
B11
64
resultierender Baum
25
144
25
B2
1
4
B5
9
144
36
9
B6
Knoten
kominieren
81
16
25
32
36
49
81
100
121
196
144
169
B10
196
B11
2.4 Objekte
Ziele der Objektorientierung: Objekte haben Eigenschaften (beschrieben durch ein oder mehrere Attribute),
haben Struktur (innerer Aufbau), und haben Methoden zu deren Manipulation. Attribute können selbst wieder
Objekte sein.
Datenstrukturen lassen sich mit Hilfe sogenannter Typenkonstruktoren aufbauen. Die Methoden sind
Programme, die auf deren Daten operieren.
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-10
Typenkonstruktoren: erlauben den Aufbau komplexer Datenstrukturen.
.
• elementare (unstrukturerte) Typen ("Atome"): Integer, Boolean, Character; elementare Aufzählungstypen
z.B. wie in Pascal, die keine für die konkrete Anwendung relevante "innere" Struktur besitzen (z.B. Farben
rot, grün, etc.).
• Standardtypen wie Real, String, ..., record und array-Typen, wenn sie semantisch als Einheit angesehen
werden sollen.
• Aggregation (Tupelkonstruktor): Aus gegebenen Typen T1 , ..., Tk wird ein neuer Typ T erzeugt, der T1, ..., Tk
als Komponententypen enthält.
• Gruppierung (Mengenkonstruktor): Aus einem gegebenen Typ wir ein neuer Typ erzeugt, dessen Instanzen
Mengen von Instanzen des Ausgangstyps sind.
• Listenkonstruktor: Aus einem gegebenen Typ wird ein neuer Typ erzeugt, dessen Instanzen Listen (Folgen)
von Instanzen des Ausgangstyps sind.
Einkapselung: legt fest, welche Daten und Methoden für den Benutzer überhaupt sichtbar sind. Die Sichtbarkeitsdefinition erfolgt über eine Schnittstelle.
Objekte: werden durch Datenstrukturen mit Attributen und Methoden charakterisiert. Die Attributwerte eines
konkreten Objekts stellen seinen "Zustand" dar.
Klassen: Menge von Objekten mit gleichen Attributen und Methoden.
Operationen (Methoden) auf Objekten:
• Erzeugen eines Objekts; nach dem Erzeugen existiert eine Referenz (Zeiger) auf den vom Objekt belegten
Speicherplatz. Die Attributwerte sind noch nicht initialisiert.
• Löschen eines Objekts
• Zuweisung, Kopie, Identität, Gleichheit, usw.
Beispiel Bücher:
Bücher
str
str
ISBN
Titel
Autoren
Versionen
Stichworte
Version
Stichwort
str
str
Autor
Aggregation
Verlag
int
int
Auflage
Jahr
Gruppierung
str
Standard Datentyp STRING
---- Ende Kapitel 2 ----
© 2002 K. Ecker
Angewandte und praktische Informatik, Kap. 2: "Höhere" Datenstrukturen
2-11
Herunterladen