• Keine Ergebnisse gefunden

3.1.5 Felder/Arrays in ML

N/A
N/A
Protected

Academic year: 2022

Aktie "3.1.5 Felder/Arrays in ML"

Copied!
33
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Felder sind ein imperatives Sprachelement. Wir betrachten sie vor allem in Kapitel 4 und 5.

Hier soll gezeigt werden,

- wie imperative und funktionale Sprachkonstrukte zusammenwirken

- wie Listen and Felder zusammenhängen

Eine Speichervariable oder einfach nur Variable ist ein Speicher/Behälter für Werte. Charakteristische Operationen auf einer Variablen v:

- Zuweisen eines Werts w an v;

- Lesen des Wertes, den v enthält/speichert/hat.

Der Zustand einer Variablen v ist undefiniert, wenn ihr noch kein Wert zugewiesen wurde; andernfalls ist der Zustand von v durch den gespeicherten Wert charakterisiert.

Begriffsklärung: (Variable)

Variablen stellen wir graphisch durch Rechtecke dar:

v: true v enthält/speichert den Wert true

3.1.5 Felder/Arrays in ML

(2)

Begriffsklärung: (eindimensionale Felder)

Ein Feld (engl. Array) ist ein n-Tupel von Variablen des gleichen Typs T. Die Variablen des Tupels

nennt man die Komponenten des Felds, T den Komponententyp. Die Komponenten sind

in ML und Java von 0 bis n-1 durchnummeriert.

ML stellt standardmäßig eine Datenstruktur für Felder bereit, die bzgl. des Elementtyps parametrisiert sind.

Typen: Ist t ein ML-Typ, dann bezeichnet t array den Typ der Felder mit Elementen aus t.

Funktionen (Auszug): (* Erklärungen: siehe unten *) array : int * t Æ t array (* Konstruktor *) fromList: t list Æ t array (* Konstruktor *) length : t array Æ int (* lesend *)

sub : t array * int Æ t (* lesend *) update : t array * int * t Æ unit

(* schreibend/modifizierend *)

Array: Eine Datenstruktur mit Modifikation

(3)

Erklärung:

array (n,x) erzeugt neues Feld mit n Komponenten;

jede Komponente wird mit x initialisiert.

fromList xl erzeugt neues Feld mit n Komponenten, wobei n die Länge der Liste xl ist;

die Komponenten werden mit den Listenelementen initialisiert.

length ar liefert die Anzahl der Feldkomponenten.

sub (ar,i) liefert den Wert, der in der i-ten Kom- ponente von ar gespeichert ist.

update (ar,i,x) speichert den Wert x an die i-te Komponente von ar. Dabei wird ar verändert!! Ergebnis ist der Wert ().

Beispiel: (Modifikation in Feldern)

> val a = fromList [1,2,3];

val a = fromList[1, 2, 3] : int Array.array

> update (a,0,7);

val it = () : unit

> a;

val it = fromList[7, 2, 3] : int Array.array

> val b = a;

val b = fromList[7, 2, 3] : int Array.array

> update (a,1,8);

val it = () : unit

> b;

(4)

Beispiel: (Funktionen mit Feldparametern)

(* swap vertauscht die Einträge in den Komponenten mit Index i und j.

Vorbedingung: 0 <= i < length a

0 <= j < length a

*)

fun swap a i j =

let val tmp = sub (a,i) in update (a,i,sub(a,j));

update (a,j,tmp) end;

(* issorted prüft, ob Feld a sortiert ist

*) fun issorted (a:int array) =

issortemb a (length a) and issortemb (a: int array) i =

if i <= 1

then true

else sub(a,i-2) <= sub (a,i-1) andalso issortemb a (i-1);

(5)

3.1.6 Signaturen und Strukturen

Begriffsklärung: (Modulsystem)

Ein Programmmodul fasst mehrere Deklarationen zusammen und stellt sie unter einem Namen zur Verfügung.

ML-Module heißen Strukturen und fassen Datentyp- und Funktionsdeklarationen zusammen.

Jede Struktur besitzt eine Signatur, die angibt, - welche Typen und

- welche Funktionen eine Struktur deklariert.

Bemerkung: (Signaturen/Strukturen)

Die Sprachkonstruktre „Signatur“ und „Struktur“

implementieren und verallgemeinern die in 3.1 eingeführten Konzepte von Datenstrukturen mit Signaturen:

- ML-Strukturen können auch Ausnahmen (engl.

Exception) und andere Strukturen enthalten

- ML-Signaturen können unabhängig von Strukturen deklariert werden.

(6)

Definition von Strukturen:

structure Environment = struct

val emptyEnv = nil;

fun lookup (n, nil) = NONE

| lookup (n, (n1,i)::env) = if n = n1 then SOME i

else lookup (n,env) fun insert ((n,i), env ) = (n,i)::env fun delete (n, nil) = nil

| delete (n, (n1,i)::env) =

if n = n1 then delete (n,env) else (n1,i)::(delete (n,env)) end;

ML inferiert aus der Strukturdefinition eine Signatur:

structure Environment :

{val 'a emptyEnv : 'a list, val ('a, 'b) insert :

('a * 'b) * ('a * 'b) list

-> ('a * 'b) list, val (''a, 'b) lookup :

''a * (''a * 'b) list -> 'b option val (''a, 'b) delete :

(7)

Beispiele: (praktische Strukturen in ML)

type elem = char

type vector = string

type instream = instream type outstream = outstream

val stdErr = <outstream> : outstream

val openAppend = fn : string -> outstream val flushOut = fn : outstream -> unit

val inputAll = fn : instream -> string val inputLine = fn : instream -> string

val output1 = fn : outstream * char ->unit val closeIn = fn : instream -> unit

val closeOut = fn : outstream -> unit

val lookahead = fn: instream->char option val stdIn = <instream> : instream

val input = fn : instream -> string val print = fn : string -> unit

val endOfStream = fn: instream -> bool

val output = fn: outstream * string ->unit val inputN = fn : instream * int -> string val openIn = fn : string -> instream

val openOut = fn : string -> outstream val stdOut = <outstream> : outstream

Auszug aus der Signatur der Struktur TextIO:

(8)

Definition und Anwendung von Signaturen:

signature STRINTENV = sig

val emptyEnv : (string,int) list val lookup :

string * (string,int) list -> int option val insert :

(string*int) * (string,int) list

-> (string,int) list end;

Signaturen sind „Typen für Strukturen“. Sie können benutzt werden, um Strukturen einzuschränken:

- auf weniger Funktionen, Werte, … - auf eingeschränktere Typen

structure StrIntEnv : STRINTENV =

Environment;

Beispiel: (Anwendung von Signaturen)

Die Struktur StrIntEnv besitzt keine Funktion delete und kann nur für (string,int)-Bindungen verarbeiten.

(9)

Anwendung von Strukturen in ML:

- Ist S eine Struktur in ML, dann kann man ihre Deklarationen d mit S.d verwenden.

- Das Kommando „open“ macht die Deklarationen eines Moduls direkt sichtbar:

open S;

(* d kann nun direkt verwendet werden *)

Beispiel: (Öffnen einer Struktur)

open StrIntEnv;

macht die Name der drei Funtionen von StrIntEnv direkt verwendbar.

(10)

3.2 Algorithmen auf Listen und Bäumen

Sortieren und Suchen sind elementare Aufgaben, die in den meisten Programmen anfallen.

Verfahren zum Suchen und Sortieren spielen eine zentrale Rolle in der Algorithmik.

Bemerkung:

Lernziele:

- Intuitiver Algorithmusbegriff

- Kenntnis wichtiger/klassischer Algorithmen und Algorithmenklassen

- Zusammenhang Algorithmus und Datenstruktur - Wege vom Problem zum Algorithmus

- Implementierungstechniken für Datenstrukturen und Algorithmen (vom Algorithmus zum Programm)

Wir führen in den Bereich Algorithmen und Datenstrukturen ausgehend vom Problem ein.

Andere Möglichkeit wäre gemäß der benutzten Datenstrukturen (Listen, Bäume, etc.).

(11)

3.2.1 Sortieren

Übersicht über 3.2:

• Sortieren

• Suchen

Sortieren ist eine Standardaufgabe, die Teil vieler speziellerer, umfassenderer Aufgaben ist.

Untersuchungen zeigen, dass „mehr als ein Viertel der kommerziell verbrauchten Rechenzeit auf

Sortiervorgänge entfällt“ [Ottmann, Widmayer:

Algorithmen und Datenstrukturen, Kap. 2].

Begriffsklärung: (Sortierproblem)

Gegeben ist eine Folge s , ..., s von sogenannten Datensätzen. Jeder Satz s hat einen Schlüssel k . Wir gehen davon aus, dass die Schlüssel ganzzahlig sind. Aufgabe des Sortierproblems ist es, eine

Permutation π zu finden, so dass die Umordnung der Sätze gemäß π folgende Reihenfolge auf den

Schlüsseln ergibt:

k ≤ k ≤ ... ≤ k

1 N

i i

π(1) π(2) π(N)

(12)

Bemerkung:

Offene Aspekte der Formulierung des Sortierproblem:

- Was heißt, eine Folge ist „gegeben“?

- Ist der Bereich der Schlüssel bekannt?

- Welche Operationen stehen zur Verfügung, um π zu bestimmen?

- Was genau heißt „Umordnung“?

Wir benutzen Datensätze folgenden Typs type dataset = int * string

mit Vergleichsoperator:

infix 4 leq

fun op leq((kx:int,dx),(ky:int,dy))= (kx<=ky)

Entwickle eine Funktion

sort: dataset list Æ dataset list

so dass sort(xl) für alle Eingaben xl aufsteigend sortiert ist. Insbesondere sind mehrfach Einträge und mehrere Einträge mit gleichem Schlüssel nicht

ausgeschlossen.

Aufgabenstellung:

(13)

Sortieren durch Auswahl (selection sort)

Algorithmische Idee:

• Entferne einen minimalen Eintrag min aus der Liste.

• Sortiere die Liste, aus der min entfernt wurde.

• Füge min als ersten Element an die sortierte Liste an.

Wir betrachten:

• Sortieren durch Auswahl (engl. selection sort)

• Sortieren durch Einfügen (engl. insertion sort)

• Bubblesort

• Sortieren durch rekursives Teilen (quick sort)

• Sortieren durch Mischen (merge sort)

• Heapsort

(14)

(* select: Hilfsfunktion

liefert einen minimalen Eintrag der Liste bzw. x, falls x minimal. *) fun select x nil = x

| select x (y::yl) = if x leq y

then select x yl else select y yl;

(* delete: Hilfsfunktion

löscht ein Vorkommen von x aus der Liste.*) fun delete x nil = nil

| delete x (y::yl) = if (x = y)

then yl

else y :: delete x yl;

(* selectionsort: sortiert Liste

min: ein minimaler Eintrag in Liste xl.

rest: die Liste xl ohne min *) fun selectionsort nil = nil

| selectionsort (x::xl) =

let val min = select x xl;

val rest = delete min (x::xl);

in

min :: (selectionsort rest) end;

(15)

Sortieren durch Einfügen (insertion sort)

Algorithmische Idee:

• Sortiere zunächst den Rest der Liste.

• Füge dann den ersten Eintrag in die sortierte Liste ein.

(* insert: Hilfsfunktion

fügt Argument in sortierte Liste ein *) fun insert a nil = [a]

| insert a (y::yl) = if (a leq y)

then a :: (y :: yl)

else y :: (insert a yl)

(* insertionsort: sortiert Liste *) fun insertionsort nil = nil

| insertionsort (x::xl) =

insert x (insertionsort xl)

(16)

Bubblesort

(* bubble: Hilfsfunktion

liefert einen maximalen Eintrag der Liste und die Liste ohne den maximalen Eintrag *) fun bubble rl e nil = (rl,e)

| bubble rl e (y::yl) = if (e leq y)

then bubble (rl@[e]) y yl else bubble (rl@[y]) e yl

(* bubblesort: sortiert Liste *) fun bubblesort nil = nil

| bubblesort (x::xl) =

let val (rl,max) = bubble nil x xl in (bubblesort rl)@[max]

end

Algorithmische Idee:

• Schiebe einen Eintrag nach rechts heraus:

- Beginne dazu mit dem ersten Eintrag e.

- Wenn schieben von e auf einen gleichen oder größeren Eintrag y stößt, schiebe y weiter.

- Ergebnis: maximaler Eintrag max und Liste ohne max

• Sortiere die Liste ohne max und hänge max an.

(17)

Quicksort: Sortieren durch Teilen

fun split p nil = (nil,nil)

| split p (x::xr) =

let val (below, above) = split p xr in if p leq x then (below,x::above)

else (x::below,above) end

fun qsort nil = nil

| qsort (p::rest) =

let val (below,above) = split p rest in (qsort below) @ [p] @ (qsort above) end

Algorithmische Idee:

• Wähle einen beliebigen Datensatz mit Schlüssel k aus, das sogenannte Pivotelement.

• Teile die Liste in zwei Teile:

- 1. Teil enthält alle Datensätze mit Schlüsseln < k - 2. Teil enthält die Datensätze mit Schlüsseln ≥ k

• Wende quicksort rekursiv auf die Teillisten an.

• Hänge die resultierenden Listen und das Pivotelement zusammen.

(18)

Bemerkung:

Quicksort ist ein typischer Algorithmus gemäß der Divide-and-Conquer-Strategie:

- Zerlege das Problem in Teilprobleme.

- Wende den Algorithmus auf die Teilprobleme an.

- Füge die Ergebnisse zusammen.

Sortieren durch Mischen:

Algorithmische Idee:

• Hat die Liste mehr als ein Element, berechne die Länge der Liste div 2 (halfsize).

• Teile die Liste in zwei Teile der Länge halfsize (+1).

• Sortiere die Teile.

• Mische die Teile zusammen.

Bemerkung:

Mergesort ist auch effizient für das Sortieren von Datensätzen, die auf externen Speichermedien liegen und nicht vollständig in den Hauptspeicher geladen werden können.

(19)

open List;

fun merge nil nil = nil

| merge nil yl = yl

| merge xl nil = xl

| merge (x::xl) (y::yl) = if (x leq y)

then x :: (merge xl (y::yl)) else y :: (merge (x::xl) yl);

(* mergesort: sortiert gegebene Liste halfsize: Hälfte der Listenlänge.

front: Vordere Hälfte der Liste.

back : Hintere Hälfte der Liste. *) fun mergesort nil = nil

| mergesort (x::nil) = [x]

| mergesort xl = let

val halfsize = (length xl) div 2;

val front = take (xl,halfsize);

val back = drop (xl,halfsize);

in (* Merge 2 sortierte Listen! *)

merge (mergesort front) (mergesort back) end;

(20)

Heapsort

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:

• 1. Schritt: Erstelle den Heap zur Eingabeliste.

• 2. Schritt:

- Entferne Maximumelement aus Heap (konstanter Aufwand) und hänge es an die Ausgabeliste.

- Stelle Heap-Bedingung wieder her (logarithmischer Aufwand).

- Fahre mit Schritt 2 fort bis der Heap leer.

Bemerkung:

• Ziele des Vorgehens:

- Beispiel für komplexe, abstrakte Datenstruktur - Zusammenhang der algorithmischen Idee und

der Datenstruktur.

• Der Begriff „Heap“ ist in der Informatik überladen.

Auch der Speicher für zur Laufzeit angelegte Variablen wird im Englischen „heap“ genannt.

(21)

Begriffsklärung: (zu Bäumen)

Ein Baum heißt markiert, wenn jeder Knoten

eine Markierung besitzt. Im Zusammenhang dieses Kapitels sind Markierungen Datensätze.

Die Höhe eines Baumes ist der maximale Abstand eines Blattes von der Wurzel; die Höhe eines

Baumes, der nur aus einem Blatt besteht, ist 1;

die Höhe des leeren Baums ist 0.

Die Tiefe eines Knotens ist sein Abstand zur Wurzel.

Die Knoten gleicher Tiefe t nennt man das Niveau t.

Ein Binärbaum ist ein Baum, in dem jeder Knoten keinen oder einen linken oder einen rechten oder einen linken und rechten Unterbaum besitzt.

Die Größe eines Baums ist die Anzahl seiner Knoten.

Ein Baum der Größe n heißt indiziert, wenn man seine Knoten mittels der Indizes 0,...,n-1 ansprechen kann.

Ein Binärbaum heißt strikt, wenn jeder Knoten ein Blatt ist oder zwei Unterbäume besitzt.

(22)

Ein Binärbaum der Höhe h heißt vollständig, wenn er strikt ist und alle Blätter die Tiefe h haben.

Ein Binärbaum der Höhe h heißt fast vollständig, wenn

- jedes Blatt die Tiefe h oder h-1 hat, - für die Knoten K des Niveaus h-1 gilt:

1. Hat K 2 Kinder, dann auch alle linken Nachbarn von K.

2. Hat K keine Kinder, dann sind auch alle rechten Nachbarn von K kinderlos.

3. Es gibt maximal ein K mit genau einem Kind, und dies ist links.

Beispiel: (Fast vollständiger indizierter und markierter Binärbaum)

88

57 2

151 42

31

0:

1: 2:

3: 4: 5:

(23)

signature FVBINTREE = sig

(* Typ fast vollständiger Binärbäume, ggf. leer *) type fvbintree;

(* Erzeugt fast vollständigen Binärbaum, wobei die Listenelemente zu Markierungen werden *) val create : dataset list -> fvbintree;

(* Anzahl der Knoten des Baums *) val size : fvbintree -> int ;

(* Markierung am Knoten mit Index i *)

val get : fvbintree * int -> dataset ;

(* Vertausche Markierungen der Knoten mit Ind. i, ,j *) val swap : fvbintree * int * int -> fvbintree;

(* Entferne letzten Knoten *)

val removeLast: fvbintree -> fvbintree ; (* Knoten mit Index i hat linkes Kind *)

val hasLeft : fvbintree * int -> bool ; (* Knoten mit Index i hat rechtes Kind *)

val hasRight : fvbintree * int -> bool

(* Index des linken Kinds von Knoten mit Index i *) val left : fvbintree * int -> int ;

(* Index des rechten Kinds von Knoten mit Index i *) val right : fvbintree * int -> int

Signatur für fast vollständige, markierte,

indizierte Binärbäume:

(24)

Vorgehen:

• Wir gehen zunächst davon aus, dass wir eine Datenstruktur zur Signatur FVBINTREE hätten und realisieren heapsort damit; d.h.

ohne die Struktur/Implementierung zu kennen.

• Wir liefern dann eine effiziente Implementierung der Signatur.

• Im Zusammenhang mit der objektorientierten

Programmierung werden wir die Implementierung weiter verfeinern.

Bemerkung:

• Wir benutzen die Datenstruktur ohne die Implemen- tierung zu kennen; man sagt die Datenstruktur ist für den Nutzer abstrakt und spricht von

abstrakter Datenstruktur.

• Die Signatur beschreibt die Schnittstelle der abstrakten Datenstruktur. Die Benutzung von Schnittstellen abstrakter Datenstrukturen ist ein zentraler Bestandteil der SW-Entwicklung.

• Eine abstrakte Datenstruktur kann unterschiedliche Implementierungen haben. Implementierungen können ausgetauscht werden, ohne dass der Nutzer seine Programme ändern muss!

(25)

Begriffsklärung: (Heap)

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 k und k , dann gilt:

k ≥ k .

N M M

N

Beispiel: (Heap)

88

57 62

25 51

42 31

10

Heap der Größe 8:

Bei einem Heap sind die Knoten entsprechend einem Breitendurchlauf indiziert (siehe Beispiel unten).

0:

1: 2:

3: 4: 5: 6:

7:

(26)

Herstellen der Heap-Eigenschaft:

Bemerkung:

Die Heap-Eigenschaft garantiert, dass der Schlüssel eines Knotens M größer gleich aller Schlüssel in den Unterbäumen von M ist. Insbesondere steht in der Wurzel ein Element mit einem maximalen Schlüssel.

Sei ein markierter, fast vollständiger Binärbaum gegeben, der die Heap-Eigenschaft nur an der Wurzel verletzt.

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.

- Gibt es genau ein Kind N, dann ist dies links und kinderlos: Ist k < k , vertausche die

Markierungen.

- Gibt es zwei Kinder und ist N das Kind mit dem größeren Schlüssel: Ist k < k , vertausche die Markierungen und fahre rekursiv mit dem

Unterbaum zu N fort.

N M

N M

(27)

Beispiel: (Versickern lassen)

62

57 18

51 42

0:

1: 2:

3: 10 4: 5:

62

57 42

51 18

0:

1: 2:

3: 10 4: 5:

18

57 62

51 42

0:

1: 2:

3: 10 4: 5:

(28)

(* Annahme: Die Kinder von ix in b erfüllen die Heap-Eigenschaft *)

fun heapify b ix =

let val ds = get(b,ix) in

if hasLeft(b,ix)

andalso not (hasRight(b,ix)) then

let val lx = left(b,ix) in if get(b,lx) leq ds

then b

else swap(b,ix,lx) end

else

if hasRight(b,ix) then

let val lx = left(b,ix) ; val rx = right(b,ix);

(* zu betrachtendes Kind *) val cx =

if get(b,lx) leq get(b,rx) then rx

else lx

in if get(b,cx) leq ds then b

else heapify (swap(b,ix,cx)) cx end

else b end

Funktion heapify formuliert auf Basis von FVBINREE:

(29)

Konkretisierung des Heapsort-Algorithmus:

1. Schritt:

- 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.

2. Schritt::

- Schreibe den Wurzel-Datensatz in die Ausgabe.

- Schreibe den Datensatz des letzten Elementes in den Wurzelknoten.

- Entferne das letzte Element.

- Stelle die Heap-Eigenschaft wieder her.

- Fahre mit Schritt 2 fort, solange die Größe > 0.

Lemma

:

In einem fast vollständigen Binärbaum der Größe n

sind die Knoten mit den Indizes (n div 2) bis n-1 Blätter.

(30)

Heapsort: Abstrakte Version

Wir betrachten zunächst Heapsort auf Basis des abstrakten Datentyps mit Signatur FVBINTREE:

Heapsort profitiert davon, dass sich fast vollständige, markierte, indizierte Binärbäume sehr effizient mit Feldern realisieren lassen:

(* Hilfsfunktion für Schritt 1 *)

fun heapifyAll b = hpfyEmb b ((size b) div 2) and hpfyEmb b 0 = if size b = 0 then b

else heapify b 0

| hpfyEmb b ix = hpfyEmb (heapify b ix) (ix-1)

(* heapsort: sortiert gegebene Liste *) fun heapsort xl =

rev (sortheap (heapifyAll (create xl))) and sortheap hp =

if size hp = 0 then []

else let val maxds = get(hp,0) ;

val hp1 = swap(hp,0,(size hp)-1);

val hp2 = removeLast hp1 ; val hp3 = heapify hp2 0 in maxds::(sortheap hp3) end;

(31)

88

57 62

25 51

42 31

10

0:

1: 2:

3: 4: 5: 6:

7:

0: 88 1: 62 2: 57 3: 31 4: 42 5: 51 6: 25 7: 10

Beachte:

Ist ix der Index eines Knotens, dann ist:

- 2*(ix+1)-1 der Index des linken - 2*(ix+1) der Index des rechten Kindes.

Beispiel: (Heaps in Feldern)

Realisierung von Heaps mit Feldern:

(32)

structure FvBinTree : FVBINTREE = struct open Array;

(* Typ fast vollständiger Binärbäume, ggf. leer.

Implementiert mit Feldern;

erste Komponente: genutzte Größe *) type fvbintree = int * (dataset array);

(* fromList: ‘a list -> ‘a array *)

fun create xl =(List.length xl,fromList xl);

fun size (s,fbt) = s;

fun get ((s,fbt),i) = sub(fbt,i);

(* swap: Modifiziert fbt *) fun swap ((s,fbt),i,j) =

let val tmp = sub(fbt,i) in update(fbt,i,sub(fbt,j));

update(fbt,j,tmp);

(s,fbt) end;

fun removeLast (s,fbt) = (s-1,fbt) fun left ((s,fbt),i) = 2*(i+1)-1 ; fun right ((s,fbt),i) = 2*(i+1) ; fun hasLeft((s,fbt),i) =

(left((s,fbt),i)) < s fun hasRight((s,fbt),i)=

(right((s,fbt),i)) < s end;

open FvBinTree;

(33)

Es hätte klar werden müssen:

• Zu einem algorithmischen Problem (hier Sortieren) gibt es im Allg. viele Lösungen.

• Lösungen unterscheiden sich in:

- der Laufzeiteffizienz (messbar) - der Speichereffizienz (messbar)

- der „Komplexität“ der Verfahrensidee (im Allg.

nicht messbar).

In den folgenden Kapiteln werden wir demonstrieren,

• wie einige der obigen Algorithmen in anderen Sprachenparadigmen formuliert werden können;

• wie der Effizienz/Komplexitätsbegriff präzisiert werden kann.

3.2.2 Suchen

Abschließende Bemerkungen zu 3.2.1

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;

Referenzen

ÄHNLICHE DOKUMENTE

nah groß fern süß sauer salzig. laut

Jahrhundert ver- breitete sich das Konzept eines definierten und aufgezeichneten Modells für einen Prozess (ein ‚Vor- gehensmodell‘) und dessen (mehr oder minder)

[r]

Was heißt denn hier Langeweile..

Weitere H¨ aufungswerte gibt es nicht, denn zu jedem anderen Punkt kann man eine so kleine Umgebung w¨ ahlen, dass nur endlich viele Folgenglieder a n in

Weitere H¨ aufungswerte gibt es nicht, denn zu jedem anderen Punkt kann man eine so kleine Umgebung w¨ ahlen, dass nur endlich viele Folgenglieder a n in

Überlegen Sie (schriftlich!) die Gründe für die jeweiligen Änderungen der Effizienz der einzelnen Methoden. Wodurch ergeben sich die Unterschiede?.. Übung Praktische

Der Suchbaum hat folgende Metho- den: insert fügt eine Zahl sortiert ein, search sucht den Knoten mit der gegebenen Zahl, delete entfernt eine Zahl und size liefert die Anzahl