Foliensatz 8 (4 auf 1)

Werbung
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Heapsort
3.2 Algorithmen auf Listen und Bäumen
Bemerkung:
Heapsort verfeinert die Idee des Sortierens durch Auswahl:
• Minimum bzw. Maximum wird nicht durch lineare Suche gefunden,
• sondern mit logarithmischem Aufwand durch Verwendung einer
besonderen Datenstruktur, dem sogenannten Heap.
Algorithmische Idee:
• Der Begriff „Heap“ist in der Informatik überladen.
• 1. Schritt: Erstelle den Heap zur Eingabeliste.
• 2. Schritt:
I Entferne Maximumelement aus Heap (konstanter Aufwand) und
hänge es an die Ausgabeliste.
I Stelle Heap-Bedingung wieder her (logarithmischer Aufwand).
I Fahre mit Schritt 2 fort bis der Heap leer.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
• Ziele des Vorgehens:
I Beispiel für komplexe, abstrakte Datenstruktur
I Zusammenhang der algorithmischen Idee und der Datenstruktur.
Auch der Speicher für zur Laufzeit angelegte Variablen wird im
Englischen „heap“genannt.
357
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Begriffsklärung: (zu Bäumen)
358
3.2 Algorithmen auf Listen und Bäumen
Begriffsklärung: (zu Bäumen) (2)
Ein Binärbaum der Höhe h heißt fast vollständig, wenn
• jedes Blatt die Tiefe h − 1 oder h − 2 hat,
Wir betrachten im Folgenden Binärbäume, die
• jeder Knoten mit einer Tiefe kleiner h − 2 zwei nicht-leere
• entweder leer sind oder
Unterbäume hat,
• für die Knoten K des Niveaus h − 2 gilt:
• aus einem markierten Knoten mit zwei Unterbäumen bestehen.
Ein Blatt ist dann ein Knoten mit zwei leeren Unterbäumen.
1. Hat K 2 nicht-leere Unterbäume, dann auch alle linken Nachbarn
von K .
2. Ist K ein Blatt, dann sind auch alle rechten Nachbarn von K Blätter.
3. Es gibt maximal ein K mit genau einem nicht-leeren Unterbaum
und der ist links.
Ein Binärbaum heißt strikt, wenn jeder Knoten ein Blatt ist oder zwei
nicht-leere Unterbäume besitzt.
Ein Binärbaum der Höhe h heißt vollständig, wenn er strikt ist und
alle Blätter die Tiefe h − 1 haben.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
Ein Baum der Größe n heißt indiziert, wenn man seine Knoten mittels
der Indizes 0, . . . , n − 1 ansprechen kann.
359
©Arnd Poetzsch-Heffter
TU Kaiserslautern
360
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Bemerkung:
3.2 Algorithmen auf Listen und Bäumen
Modulschnittstelle für fast vollständige, markierte,
indizierte Binärbäume:
Im Folgenden gehen wir bei indizierten Bäumen immer davon aus,
dass die Reihenfolge der Indices einem Breitendurchlauf folgt (siehe
Beispiel).
module FvBintree (FVBintree ,create ,size ,get ,swap ,
removeLast ,hasLeft ,hasRight ,left ,right ) where
Beispiel: (Fast vollst., indizierter und markierter Binärbaum)
create :: [ Dataset ] -> FVBintree
-- Erzeugt fast vollstaendigen Binaerbaum , wobei die
-- Schluessel der Listenelemente zu Markierungen werden
size :: FVBintree -> Int
-- Anzahl der Knoten des Baums
get :: FVBintree -> Int -> Dataset
-- Liefert Markierung am Knoten mit Index i,
-- 0 <= i < size b
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
361
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Modulschnittstelle für fast vollständige, markierte,
indizierte Binärbäume: (2)
362
3.2 Algorithmen auf Listen und Bäumen
Modulschnittstelle für fast vollständige, markierte,
indizierte Binärbäume: (3)
swap :: FVBintree -> Int -> Int -> FVBintree
-- Vertauscht Markierungen der Knoten mit Index i und j
-- Modifiziert fbt; 0 <= i,j < size b
removeLast :: FVBintree -> FVBintree
-- Entfernt letzten Knoten
left :: FVBintree -> Int -> Int
-- Liefert Index des linken Kinds von Knoten mit Index i
-- 0 <= i < size b && hasLeft i
hasLeft :: FVBintree -> Int -> Bool
-- Knoten mit Index i hat linkes Kind
-- 0 <= i < size b
right :: FVBintree -> Int -> Int
-- Liefert Index des rechten Kinds von Knoten mit Index i
-- 0 <= i < size b && hasRight i
hasRight :: FVBintree -> Int -> Bool
-- Knoten mit Index i hat rechtes Kind
-- 0 <= i < size b
©Arnd Poetzsch-Heffter
TU Kaiserslautern
363
©Arnd Poetzsch-Heffter
TU Kaiserslautern
364
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Vorgehen
3.2 Algorithmen auf Listen und Bäumen
Bemerkung:
• Wir benutzen die Datenstruktur ohne die Implementierung zu
kennen; man sagt die Datenstruktur ist für den Nutzer abstrakt
und spricht von abstrakter Datenstruktur.
• Wir gehen im Folgenden davon aus, dass wir eine
Implementierung von FvBintree haben (steht zum Testen bereit)
und realisieren heapsort damit; d.h. ohne die
• Die Signatur beschreibt die Schnittstelle der abstrakten
Struktur/Implementierung zu kennen.
Datenstruktur. Die Benutzung von Schnittstellen abstrakter
Datenstrukturen ist ein zentraler Bestandteil der SW-Entwicklung.
• Im Zusammenhang mit der objektorientierten Programmierung
werden wir dann eine sehr effiziente Implementierung für fast
vollständige Binärbäume kennen lernen.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
• Eine abstrakte Datenstruktur kann unterschiedliche
Implementierungen haben. Implementierungen können
ausgetauscht werden, ohne dass der Nutzer seine Programme
ändern muss!
365
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Begriffsklärung: (Heap)
366
3.2 Algorithmen auf Listen und Bäumen
Beispiel: (Heap)
Heap der Größe 8:
Ein markierter, fast vollständiger, indizierter Binärbaum mit n Knoten
heißt ein Heap der Größe n, wenn die folgende Heap-Eigenschaft
erfüllt ist:
Ist M ein Knoten und N ein Kind von M mit Markierungen kM und kN ,
dann gilt:
kM ≥ kN
Bei einem Heap sind die Knoten entsprechend einem Breitendurchlauf
indiziert (siehe Beispiel unten).
©Arnd Poetzsch-Heffter
TU Kaiserslautern
367
©Arnd Poetzsch-Heffter
TU Kaiserslautern
368
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Bemerkung:
3.2 Algorithmen auf Listen und Bäumen
Herstellen der Heap-Eigenschaft:
Sei ein markierter, fast vollständiger Binärbaum gegeben, der die
Heap-Eigenschaft nur an der Wurzel verletzt.
Die Heap-Eigenschaft garantiert, dass der Schlüssel eines Knotens M
größer gleich aller Schlüssel in den Unterbäumen von M ist.
Die Heap-Eigenschaft kann hergestellt werden, indem man den
Wurzelknoten M rekursiv in dem Unterbaum mit dem größeren
Schlüssel versickern lässt:
• Gibt es kein Kind, ist nichts zu tun.
Insbesondere steht in der Wurzel ein Element mit einem maximalen
Schlüssel.
• Gibt es genau ein Kind N, dann ist dies links und kinderlos:
Ist kM < kN , vertausche die Markierungen.
• Gibt es zwei Kinder und ist N das Kind mit dem größeren
Schlüssel:
Ist kM < kN , vertausche die Markierungen und fahre rekursiv mit
dem Unterbaum zu N fort.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
369
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
TU Kaiserslautern
3. Funktionales Programmieren
Beispiel: (Versickern lassen)
©Arnd Poetzsch-Heffter
©Arnd Poetzsch-Heffter
370
3.2 Algorithmen auf Listen und Bäumen
Beispiel: (Versickern lassen) (2)
371
©Arnd Poetzsch-Heffter
TU Kaiserslautern
372
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Beispiel: (Versickern lassen) (3)
3.2 Algorithmen auf Listen und Bäumen
Funktion heapify formuliert auf Basis von FVBINREE:
heapify :: FVBintree -> Int -> FVBintree
-- Stelle Heap - Eigenschaft im Knoten ix her
-- Annahme : die Kinder von ix erfuellen die
-- Heap - Eigenschaft
heapify b ix =
let ds = get b ix
in if hasLeft b ix && not ( hasRight b ix)
then let lx = left b ix
in if get b lx `leq` ds
then b
else swap b ix lx
-- else ...
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
373
3.2 Algorithmen auf Listen und Bäumen
374
3.2 Algorithmen auf Listen und Bäumen
Konkretisierung des Heapsort-Algorithmus:
rechten Unterbaum
1. Schritt:
left b ix
right b ix
if get b lx `leq` get b rx
then rx
else lx
in if get b largerKid `leq` ds
then b
else heapify (swap b ix largerKid ) largerKid
else b
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
Funktion heapify formuliert auf Basis von FVBINREE:
(2)
else -- hat linken und
-- oder ist Blatt
if hasRight b ix
then let lx
=
rx
=
largerKid =
©Arnd Poetzsch-Heffter
TU Kaiserslautern
375
• Erzeuge Binärbaum-Repräsentation aus Eingabefolge.
• Stelle Heap-Eigenschaft her, indem heapify ausgehend von den
Blättern für jeden Knoten aufgerufen wird. Es reicht, nur Knoten
mit Kindern zu berücksichtigen.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
376
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Konkretisierung des Heapsort-Algorithmus: (2)
Heapsort: Abstrakte Version
2. Schritt:
Wir betrachten zunächst Heapsort auf Basis des abstrakten Datentyps
für markierte, fast vollständige, indizierte Binärbaume:
• Schreibe den Wurzel-Datensatz in die Ausgabe.
• Schreibe den Datensatz des letzten Elementes in den
heapifyAll :: FVBintree -> FVBintree
hpfyEmb
:: FVBintree -> Int -> FVBintree
-- Hilfsfunktionen fuer den ersten Schritt
Wurzelknoten (swap)
• Entferne das letzte Element.
• Stelle die Heap-Eigenschaft wieder her.
heapifyAll b = hpfyEmb b (( size b) `div` 2)
• Fahre mit Schritt 2 fort, solange die Größe > 0.
hpfyEmb b 0
Lemma:
In einem fast vollständigen Binärbaum der Größe n sind die Knoten mit
den Indizes (ndiv 2) bis n − 1 Blätter.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
377
then b
else heapify b 0
hpfyEmb b ix = hpfyEmb ( heapify b ix) (ix -1)
if size b == 0
TU Kaiserslautern
3. Funktionales Programmieren
378
3.2 Algorithmen auf Listen und Bäumen
Bemerkungen:
-- heapsort : sortiert gegebene Liste
heapsort :: [ Dataset ] -> [ Dataset ]
• Wie wir in Kapitel 4 zeigen, profitiert Heapsort davon, dass sich
heapsort xl = reverse ( sortheap ( heapifyAll ( create xl)))
sortheap :: FVBintree -> [ Dataset ]
sortheap hp =
if
size hp == 0
then []
else let maxds = get hp 0
hp1
= swap hp 0 (size hp - 1)
hp2
= removeLast hp1
hp3
= heapify hp2 0
in maxds : ( sortheap hp3)
TU Kaiserslautern
=
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
Heapsort: Abstrakte Version (2)
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
fast vollständige, markierte, indizierte Binärbäume sehr effizient
mit Feldern realisieren lassen.
• Zu einem algorithmischen Problem (hier Sortieren) gibt es im Allg.
viele Lösungen.
• Algorithmische Lösungen unterscheiden sich in:
I der Laufzeiteffizienz (messbar)
I der Speichereffizienz (messbar)
I der „Komplexität“ der Verfahrensidee (im Allg. nicht messbar).
379
©Arnd Poetzsch-Heffter
TU Kaiserslautern
380
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Bemerkungen: (2)
3.2 Algorithmen auf Listen und Bäumen
Unterabschnitt 3.2.2
In den folgenden Kapiteln werden wir demonstrieren,
• wie einige der obigen Algorithmen in anderen
Suchen
Programmierparadigmen formuliert werden können;
• wie der Effizienz/Komplexitätsbegriff präzisiert werden kann.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
381
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Suchen
382
3.2 Algorithmen auf Listen und Bäumen
Schnittstell zum Suchen
Wir betrachten die folgende Schnittstelle:
module Dictionary (Dict ,emptyDict ,get ,put , remove ) where
Die Verwaltung von Datensätzen basiert auf drei grundlegenden
Operationen:
• Einfügen eines Datensatzes in eine Menge von Datensätzen;
• Suchen eines Datensatzes mit Schlüssel k ;
type Dict = STree
-- type des Dictionarys
emptyDict :: Dict
-- leeres Dictionary
get :: Dict -> Int -> (Bool , String )
-- Nachschauen des Eintrags zu Schluessel i
• Löschen eines Datensatzes mit Schlüssel k .
Bemerkung:
Weitere oft gewünschte Operationen sind: Sortierte Ausgabe, Suchen
aller Datensätze mit bestimmten Eigenschaften, Bearbeiten von Daten
ohne eindeutige Schlüssel, etc.
put :: Dict -> Int -> String -> Dict
-- Einfuegen des Eintrags (i,s),
-- Ueberschreibt ggf. alten Eintrag zu i
remove :: Dict -> Int -> Dict
-- Loeschen des Eintrags zu Schluessel i
©Arnd Poetzsch-Heffter
TU Kaiserslautern
383
©Arnd Poetzsch-Heffter
TU Kaiserslautern
384
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Bemerkung:
3.2 Algorithmen auf Listen und Bäumen
Begriffsklärung: (Binärer Suchbaum)
In der Literatur zur funktionalen Programmierung wir „get“ oft „lookup“
oder „search“, „put“ oft „insert“ und „remove“ oft „delete“genannt.
Ein markierter Binärbaum B ist ein natürlicher binärer Suchbaum
(kurz: binärer Suchbaum), wenn die Suchbaum-Eigenschaft gilt, d.h.
wenn für jeden Knoten K in B gilt:
Um den Zusammenhang zu OO-Schnittstellen augenfälliger zu
machen, benutzen wir die dort üblichen Namen.
Ziel ist es, Datenstrukturen zu finden, bei denen der Aufwand für obige
Operationen gering ist. Wir betrachten hier die folgenden
Dictionary-Realisierungen:
• Alle Schlüssel im linken Unterbaum von K sind echt kleiner als der
Schlüssel von K .
• Alle Schlüssel im rechten Unterbaum von K sind echt größer als
der Schlüssel von K .
• lineare Datenstrukturen (Übung)
• (natürliche) binäre Suchbäume (Vorlesung)
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
385
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Bemerkung:
386
3.2 Algorithmen auf Listen und Bäumen
Datenstruktur für Suchbäume:
Wir stellen Dictionaries als Binärbäume mit Markierungen vom Typ
Dataset dar:
• „Natürlich“ bezieht sich auf das Entstehen der Bäume in
Abhängigkeit von der Reihenfolge der Einfüge-Operationen
(Abgrenzung zu balancierten Bäumen).
data STree =
Node Dataset STree STree
Empty
deriving (Eq , Show)
• In einem binären Suchbaum gibt es zu einem Schlüssel maximal
einen Knoten mit entsprechender Markierung.
|
emptyDict = Empty
Die Konstante emptyDict repräsentiert das leere Dictionary.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
387
©Arnd Poetzsch-Heffter
TU Kaiserslautern
388
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Invariante für Suchbäume:
3.2 Algorithmen auf Listen und Bäumen
Suchen eines Eintrags:
Binärbäume, die als Dictionary verwendet werden, müssen die
Suchbaum-Eigenschaft erfüllen.
Alle Funktionen, die Dictionaries als Parameter bekommen, gehen
davon aus, dass die Suchbaum-Eigenschaft für die Parameter gilt.
Wenn kein Eintrag zum Schlüssel existiert, liefere (False,""); sonst
liefere (True,s), wobei s der String zum Schlüssel ist:
Die Funktionen müssen garantieren, dass die Eigenschaft auch für
Ergebnisse gilt.
get
get
|
|
|
Man sagt:
Die Suchbaum-Eigenschaft ist eine Datenstrukturinvariante von
Dictionaries.
Empty k
= (False ,"")
(Node (km ,s) l r) k
k < km
=
get l k
km < k
=
get r k
otherwise =
(True ,s)
Wir guarantieren die Datenstrukturinvariante u.a. dadurch, dass wir
Nutzern des Moduls Dictionary keinen Zugriff auf die Konstruktoren
geben.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
389
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Einfügen eines Eintrags:
390
3.2 Algorithmen auf Listen und Bäumen
Beispiel:
Algorithmisches Vorgehen:
Einfügen von 33:
• Neue Knoten werden immer als Blätter eingefügt.
• Die Position des Blattes wird durch den Schlüssel des neuen
Eintrags festgelegt.
• Beim Aufbau eines Baumes ergibt der erste Eintrag die Wurzel.
• Ein Knoten wird
I in den linken Unterbaum der Wurzel eingefügt, wenn sein Schlüssel
kleiner ist als der Schlüssel der Wurzel;
I in den rechten, wenn er größer ist.
Dieses Verfahren wird rekursiv fortgesetzt, bis die Einfügeposition
bestimmt ist.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
391
©Arnd Poetzsch-Heffter
TU Kaiserslautern
392
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Implementierung von put:
3.2 Algorithmen auf Listen und Bäumen
Bemerkungen:
• Die Reihenfolge des Einfügens bestimmt das Aussehen des
Die algorithmische Idee lässt sich direkt umsetzen.
binären Suchbaums:
Beachte aber, dass das Dictionary nicht verändert wird, sondern ein
neues erzeugt und abgeliefert wird:
Reihenfolgen:
2;3;1
put Empty k s
= Node (k,s)
put (Node (km ,sm) l r) k s
| k == km
= Node (k,s) l
| k < km
= Node (km ,sm)
| otherwise = Node (km ,sm)
©Arnd Poetzsch-Heffter
1;2;3
Empty Empty
r
(put l k s) r
l (put r k s)
TU Kaiserslautern
3. Funktionales Programmieren
1;3;2
393
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Bemerkungen: (2)
394
3.2 Algorithmen auf Listen und Bäumen
Löschen:
Löschen ist die schwierigste Operation, da
• Es gibt sehr viele Möglichkeiten, aus einer vorgegebenen
• ggf. innere Knoten entfernt werden und dabei
Schlüsselmenge einen binären Suchbaum zu erzeugen.
• die Suchbaum-Eigenschaft erhalten werden muss.
• Bei sortierter Einfügereihenfolge entartet der binäre Suchbaum
zur linearen Liste.
Algorithmisches Vorgehen:
• Der Algorithmus zum Einfügen ist schnell, insbesondere weil
• Die Position eines zu löschenden Knotens K mit Schlüssel X wird
keine Ausgleichs- oder Reorganisationsoperationen
vorgenommen werden müssen.
nach dem gleichen Verfahren wie beim Suchen eines Knotens
bestimmt.
• Dann sind drei Fälle zu unterscheiden:
©Arnd Poetzsch-Heffter
TU Kaiserslautern
395
©Arnd Poetzsch-Heffter
TU Kaiserslautern
396
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
Löschen: (2)
Löschen: (3)
1. Fall: K ist ein Blatt.
2. Fall: K mit Schlüssel X hat genau einen Unterbaum.
Lösche K :
K wird im Eltern-Knoten durch sein Kind ersetzt und gelöscht:
Entsprechend, wenn Knoten mit Schlüssel X in rechtem Unterbaum.
Die anderen links-rechts-Varianten entsprechend.
©Arnd Poetzsch-Heffter
TU Kaiserslautern
3. Funktionales Programmieren
397
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Löschen: (4)
398
3.2 Algorithmen auf Listen und Bäumen
Löschen: (5)
3. Fall: K mit Schlüssel X hat genau zwei Unterbäume.
Problem:
Wo werden die beiden Unterbäume nach dem Löschen von K
eingehängt?
Hier gibt es 2 symmetrische Lösungsvarianten:
• Ermittle den Knoten KR mit dem kleinsten Schlüssel im rechten
Unterbaum, Schlüssel von KR sei XR.
• Speichere XR und die Daten von KR in K .
• Lösche KR gemäß Vorgehen zu Fall 1 bzw. 2, möglich da für KR
einer der Fälle zutrifft.
(andere Variante: größten Schlüssel im rechten UB)
©Arnd Poetzsch-Heffter
TU Kaiserslautern
399
©Arnd Poetzsch-Heffter
TU Kaiserslautern
400
3. Funktionales Programmieren
3.2 Algorithmen auf Listen und Bäumen
3. Funktionales Programmieren
Umsetzung in Haskell
Umsetzung in Haskell (2)
removemin :: STree -> (Dataset , STree)
-- Parameter : nichtleerer binaerer Suchbaum b.
-- Liefert Eintrag d mit kleinstem Schluessel in b
-- und Baum nach Loeschen von d in b
• Die Fälle 1 und 2 lassen sich direkt behandeln.
• Für Fall 3 realisiere Hilfsfunktion removemin, die
I nichtleeren binären Suchbaum b als Parameter nimmt;
I ein Paar (mnm, br ) als Ergebnis liefert, wobei
I
I
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
removemin (Node d Empty r) = (d,r)
removemin (Node d l r) =
let (mnm ,ll) = removemin l
in (mnm , Node d ll r)
mnm der kleinste Datensatz in b ist und
br der Baum ist, der sich durch Löschen von mnm aus b ergibt.
TU Kaiserslautern
3. Funktionales Programmieren
401
©Arnd Poetzsch-Heffter
3.2 Algorithmen auf Listen und Bäumen
TU Kaiserslautern
3. Funktionales Programmieren
Umsetzung in Haskell (3)
402
3.2 Algorithmen auf Listen und Bäumen
Diskussion:
Der Aufwand für die Grundoperationen Einfügen, Suchen und Löschen
eines Knotens ist proportional zur Tiefe des Knotens, bei dem die
Operation aus- geführt wird.
Ist h die Höhe des Suchbaumes, ist der Aufwand der
Grundoperationen im ungünstigsten Fall also O(h), wobei
remove Empty k
= Empty
remove (Node (km ,s) l r) k
| k < km
= Node (km ,s) ( remove l k) r
| km < k
= Node (km ,s) l ( remove r k)
| l == Empty = r
| r == Empty = l
| otherwise =
-- k == km && l /= Empty /= r
let (mnm ,rr) = removemin r
in Node mnm l rr
log(N + 1) ≤ h ≤ N
für Knotenanzahl N.
Folgerung:
Bei degenerierten natürlichen Suchbäumen kann linearer Aufwand für
alle Grundoperationen entstehen.
Im Mittel verhalten sich Suchbäume aber wesentlich besser. Zusätzlich
versucht man durch gezielte Reorganisation eine gute Balancierung zu
erreichen (siehe Kapitel 5).
©Arnd Poetzsch-Heffter
TU Kaiserslautern
403
©Arnd Poetzsch-Heffter
TU Kaiserslautern
404
Herunterladen