Praktische Informatik I – Algorithmen und Datenstrukturen 8 Wintersemester 2006/07 Weitere Algorithmen Algorithmenmuster (Design Patterns): Scanline, Rekursion, Divide–and–Conquer, Graphendurchlauf: DFS, BFS, Backtracking in Verbindung mit DFS, Greedy, Dynamische Programmierung. Prof. Dr. Dietmar Seipel 416 Praktische Informatik I – Algorithmen und Datenstrukturen 8.1 Wintersemester 2006/07 Das Algorithmenmuster Greedy greedy: engl. für gierig Das Prinzip “gieriger” Algorithmen ist es, in jedem Teilschritt so viel wie möglich zu erreichen. Bei einigen Problemen wie der Berechnung kürzester Wege oder der Suche nach einem aufspannenden Baum in einem Graphen kann man mit einer geeigneten Greedy–Methode die Optimallösung finden. Bei anderen Problemen wie etwa dem Rucksackproblem oder dem Problem des Handlungsreisenden erreicht man damit aber meist nicht die Optimallösung. Prof. Dr. Dietmar Seipel 417 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Berechnung kürzester Wege auf Distanzgraphen – Algorithmus von Dijkstra Anwendung von Vorangswarteschlangen , dessen Kanten Gegeben sei ein gerichteter Graph markiert sind (Distanzgraph). nicht–negativen Längen fixiert. Desweiteren wird ein Knoten mit von zu Prof. Dr. Dietmar Seipel von kürzesten Wegen Gesucht sind die Längen allen anderen Knoten . 418 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Beispiel: Prof. Dr. Dietmar Seipel 419 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Dann gilt: Prof. Dr. Dietmar Seipel 420 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Greedy–Algorithmus, Dijkstra Setze , , , für alle . Dann wird nach dem Prinzip „Knoten mit kürzester Distanz von zuerst“ erhält: schrittweise vergrößert, bis man . mit minimaler Distanz 1. Wähle Knoten zu hinzu ist bereits korrekt!) durch: , ersetze zu von 3. Füre jede Kante 2. Nimm ( Prof. Dr. Dietmar Seipel 421 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 eines Prof. Dr. Dietmar Seipel Für alle Kanten aus kennt man bereits die Länge kürzesten Weges von nach . 422 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Wir implementieren als Vorrangswarteschlange (Priority Queue). Die . Prioritäten sind die Distanzen Dann werden die 3 Schritte aus dem Algorithmus von Dijkstra folgendermaßen realisiert: 1. Löschen des Minimums. 2. Einfügen. 3. Verringerung der Priorität eines Schlüssels. Prof. Dr. Dietmar Seipel 423 Praktische Informatik I – Algorithmen und Datenstrukturen 8.2 Wintersemester 2006/07 Algorithmen zum Graphendurchlauf Tiefensuche (Depth First Search, DFS) Breitensuche (Breadth First Search, BFS) Anwendung zur Suche in Labyrinthen Prof. Dr. Dietmar Seipel 424 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 repräsentiert: Das Labyrinth wird als Graph Wir lassen der Einfachheit halber die inversen Kanten des Labyrinths weg. Prof. Dr. Dietmar Seipel 425 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Rahmenalgorithmus zum Graphendurchlauf , , Startknoten Graph und Teilmengen , für Knotenmenge Datenstruktur Zielknotenmenge . Es werden kürzeste Wege von zu allen Knoten aus Prof. Dr. Dietmar Seipel berechnet. 426 Praktische Informatik I – Algorithmen und Datenstrukturen ; ein; in füge Wintersemester 2006/07 ; solange noch Elemente enthält wähle das erste Element aus falls es keine Kante mit Knoten dann lösche aus sonst { benutze eine Kante , d.h. ; falls dann { ; füge in ein; Vorgänger ; falls dann breche den Algorithmus ab } } gibt Prof. Dr. Dietmar Seipel 427 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Suche nach einem einzigen Zielknoten in JAVA class GraphNode { GraphNode[] neighbors; boolean isVisited; int key; public GraphNode(int key) { this.key = key; } public void setNeighbors(GraphNode... neighbors) { this.neighbors = neighbors; } Prof. Dr. Dietmar Seipel 428 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 public List findPath(GraphNode destination) { if (isVisited) return null; isVisited = true; List path = null; if (destination.key == this.key) { path = new List(); path.add(key); } else { for (int i = 0; i < neighbors.length; i++) { path = neighbors[i].findPath(destination); if (path != null) { path.insert(key, null); break; } } } return path; } Prof. Dr. Dietmar Seipel 429 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 public static void main(String[] args) { GraphNode[] nodes = new GraphNode[5]; for (int i = 0; i < nodes.length; i++) { nodes[i] = new GraphNode(i); } nodes[0].setNeighbors(nodes[1]); nodes[1].setNeighbors(nodes[2], nodes[0]); nodes[2].setNeighbors(nodes[3], nodes[4]); nodes[3].setNeighbors(nodes[4]); nodes[4].setNeighbors(nodes[1]); System.out.println(nodes[4].findPath(nodes[0])); } } Prof. Dr. Dietmar Seipel 430 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Tiefensuche: Falls Border als Stapel realisiert wird, so ist das erste Element von Border immer das oberste und wir fügen ein indem wir auf den Stapel legen. Dann liefert der Rahmenalgorithmus eine Tiefensuche. Beispiel: Prof. Dr. Dietmar Seipel 431 Praktische Informatik I – Algorithmen und Datenstrukturen . und Sei Wintersemester 2006/07 Die Knoten werden in der Reihenfolge ihrer Nummern besucht. benutzt. wird zuerst die Kante Für Für gibt es keine Kanten nächsten unbenutzten Kante für . Deshalb fahren wir mit der fort (Backtracking) . Dann wird analog zu behandelt, und wir gelangen schließlich über den Knotem 8 zum Ziel 9. Die berechnete Vorgängerfunktion ist jetzt wie folgt: 1 3 4 5 6 7 8 9 1 2 3 4 4 6 6 8 Vorgänger 2 Der berechnete Weg vom Start zum Ziel ist also: Prof. Dr. Dietmar Seipel 432 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Breitensuche: Falls Border als Priority Queue (First In / First Out) realisiert wird, so liefert der Rahmenalgorithmus eine Breitensuche und . Sei weiter Auch hier werden die Knoten wieder in der Reihenfolge ihrer Nummern besucht und der berechnete Weg ist der derselbe. Aber jetzt werden für zuerst die beiden Kanten und benutzt. Die Priority Queue verändert sich wie folgt: Benutzen von Benutzen von Prof. Dr. Dietmar Seipel Erst jetzt wird 4 gelöscht, und wir besuchen . 433 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Bei der Tiefensuche verändert sich Border dagegen wie folgt: Löschen von Benutzen von Benutzen von Benutzen von Löschen von In der Tat ist der Stack am Ende gleich d.h. gleich dem inversen gefundenen Weg vom Start zum Ziel. Prof. Dr. Dietmar Seipel 434 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Falls die Sackgasse bei als die Tiefensuche. Prof. Dr. Dietmar Seipel länger wäre, so wäre die Breitensuche teurer 435 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Die Tiefensuche benutzt die Kanten in folgender Reihenfolge: Die Breitensuche benutzt dagegen nur folgende Kanten: Die Kanten zwischen 11 und 13 werden gar nicht benutzt. Prof. Dr. Dietmar Seipel 436 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 In vielen praktischen Fällen werden sogar unterschiedliche Wege berechnet: , Tiefensuche: Breitensuche: Die Breitensuche brechnet immer einen kürzesten Weg, wenn man die Kantenlängen alle auf 1 setzt. In diesem Fall entspricht der Graphendurchlauf dem Algorithmus von Dijkstra. Prof. Dr. Dietmar Seipel 437 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Tiefensuche in Graphen mit Zyklen Benutzung der Kanten: (1,2), (2,3), (3,4), (4,5), (3,5), (3,6), (6,2), (6,5). Die 5 unterstrichenen Kanten bilden einen Baum. Prof. Dr. Dietmar Seipel 438 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Die weiteren Kanten heißen: Vorwärtskanten: Rückwärtskanten: Prof. Dr. Dietmar Seipel Seitwärtskanten: 439 Praktische Informatik I – Algorithmen und Datenstrukturen 8.3 Wintersemester 2006/07 Tiefensuche und Backtracking in P ROLOG P ROLOG ermöglicht deklarative Programmierung kompakte Programme rapid Prototyping Die Auswertungsstrategie der SLDNF–Resolution benutzt Tiefensuche Backtracking Unifikation Prof. Dr. Dietmar Seipel 440 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Datenstrukturen in P ROLOG P ROLOG–Atom: z.B. book, george, ‘George‘, x12op Konstante: P ROLOG–Atom Number z.B. book, 0, 123, 4 Variable: beginnend mit Großbuchstaben oder mit “_” z.B. Book, Up, Var, _, _X Funktor (Funktions– bzw. Prädikatensymbol): Operator P ROLOG–Atom z.B. +, –, , ancestor Prof. Dr. Dietmar Seipel Struktur: Konstante Variable Funktor(Struktur, z.B. +(1,7), ancestor(bill,joe) Listen sind spezielle Strukturen zum Funktor ’.’: [a,b,c,d] = ’.’(a, [b,c,d]) +(1,7) ist äquivalent zu 1+7 (Infix–Notation). , Struktur) 441 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 P ROLOG–Klauseln (Prädikatensymbole: ancestor, parent, likes, woman, nice, loves) " # ! :– :– Regeln: Kopf :– Rumpf, mit Konjunktion “,”, Disjunktion “;” :– Bedeutung: Fakten: ! ! # Goal: $ Prof. Dr. Dietmar Seipel 442 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Ein P ROLOG–Interpreter versucht ein Goal durch Inferenz zu beweisen. Dies erfolgt Top–Down vom Goal ausgehend durch DFS–Durchlauf des SLD–Baumes. Beispiel: ?- likes(george,Y). Y = mary Yes ?- likes(X, Y). X = george, Y = mary ; X = mary, Y = george Yes ?- Prof. Dr. Dietmar Seipel 443 $ " $ " $ " %$ #" ! & & ) ' ) ' ) ' ) ' ) ' success & )* '( 444 Prof. Dr. Dietmar Seipel success failure failure Wintersemester 2006/07 Praktische Informatik I – Algorithmen und Datenstrukturen SLD–Baum Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Anfragen in P ROLOG Die Auswertung von P ROLOG–Regeln erfolgt Top–Down mittels der Methode der SLDNF–Resolution: d.h., aus einer Anfrage an das Kopfatom einer Regel werden Unteranfragen an die Rumpfatome der Regel erzeugt. Dies entspricht dem Vorgehen bei der Abarbeitung von prozeduralen Programmiersprachen. Falls ein Rumpfatom mehrere Antworten liefert, so wird zuerst mit einer solchen Antwort weitergerechnet (Tiefensuche); falls die Berechnung später fehlschlägt, so kann mit der nächsten Antwort fortgefahren werden (Backtracking). In der Praxis sind viele P ROLOG–Prädikate aber funktional; d.h., aus einer Eingabe wird genau eine Ausgabe erzeugt. Prof. Dr. Dietmar Seipel 445 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Kontrollprimitive, welche über die PL1 hinausgehen: “assert” und “retract” verändern die interne Datenbank von P ROLOG; “clause” sucht in dieser. “call” ruft Goals auf, die vorher zusammengebaut werden können. Der Cut “!” friert die bisherige Variablenbelegung in der aktuellen Regel ein. “fail” ist ein Goal, das immer fehl schlägt. Prof. Dr. Dietmar Seipel 446 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Literatur W.F. Clocksin, C.S. Mellish: Programming in P ROLOG, Springer, 1987. I. Bratko: P ROLOG – Programming for Artificial Intelligence, Addison–Wesley, Second Edition, 1990. S. Ceri, G. Gottlob, L. Tanca: Logic Programming and Databases, Springer, 1990. J.W. Lloyd: Foundations of Logic Programming, Springer, Second Edition, 1987. Prof. Dr. Dietmar Seipel 447 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Listen sind spezielle P ROLOG–Strukturen mit dem binären Funktor ‘.’. Liste: [a,b,c,d] = ’.’(a, ’.’(b, ’.’(c, ’.’(d, [ ])))) Listenkonstruktion: Falls X und Xs bereits gebunden (mit Werten belegt) sind, so ist [X|Xs] die Liste, die man erhält, wenn man das Element X an die Liste Xs vorne anhängt. Für X = a und Xs = [b,c,d] ist [X|Xs] = [a,b,c,d]. Listenzerlegung: Falls X und Xs ungebunden sind, so liefert der Aufruf [X|Xs] = [a,b,c,d] die Belegungen X = a und Xs = [b,c,d]; man kann eine Liste also in ihren Kopf X und den Rest Xs zerlegen. leere Liste: [ ] Prof. Dr. Dietmar Seipel 448 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Rekursion: das Prädikat member /* member(?X, +L) <X is a member of the list L. */ member(X, [X|_]). member(X, [_|Xs]) :member(X, Xs). ?- member(a, [c, b, a]). Yes Prof. Dr. Dietmar Seipel 449 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Relationales Programmieren ?- member(b, [a, b, c]). Yes ?- member(X, [a, b, c]). X = a; X = b; X = c Yes Beim Aufruf member(X, Y) sind X und Y nicht funktional voneinander abhängig. Stattdessen realisiert member/2 eine n:m–Beziehung (Relation) zwischen Listen und ihren Elementen. Prof. Dr. Dietmar Seipel 450 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Das Prädikat member/2 definiert eine virtuelle Relation: member Element List a [a, b, c] b [a, b, c] c [a, b, c] a [a, b] Prof. Dr. Dietmar Seipel 451 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Suche in Graphen _ /* graph_search(+Node, -Path) <*/ graph_search(X, Path) :graph_search(X, [X], Path). graph_search(X, _, [X]) :graph_sink(X). graph_search(X, Visited, [X|Path]) :( graph_edge(X, Y) ; graph_edge(Y, X) ), not(member(Y, Visited)), write(user, ’->’), write(user, Y), graph_search(Y, [Y|Visited], Path). Prof. Dr. Dietmar Seipel 452 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Labyrinth: b c d e f g h i a graph_edge(i, graph_edge(i, graph_edge(h, graph_edge(g, graph_edge(d, graph_edge(d, graph_edge(a, graph_edge(b, f). h). g). d). e). a). b). c). graph_source(i). graph_sink(c). Prof. Dr. Dietmar Seipel 453 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 ?- graph_search(i, Path). ->f->h->g->d->e->a->b->c Path = [i, h, g, d, a, b, c] Yes ?- graph_search(e, Path). ->d->a->b->c Path = [e, d, a, b, c] ; ->g->h->i->f No ?- Prof. Dr. Dietmar Seipel 454 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Das Prädikat graph_search/2 benutzt Tiefensuche, und es berechnet einfache Pfade (ohne doppelte Knoten). Beim Aufruf graph_search(Node, Path) mit einem gebundenen Argument Node und einem freien Argument Path können alle einfache Pfade von Node zu einer Senke (graph_sink) über Backtracking berechnet werden. Würde man eine weitere Kante graph_edge(e, b) in den Graphen einfügen (die Wand zwischen e und b einreisen), so gäbe es einen weiteren einfachen Pfad [e, b, c] von e zur Senke c. Prof. Dr. Dietmar Seipel 455 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Man kann alle Antworten mittels Backtracking und findall/3 bestimmen: ... graph_edge(e, b). ?- findall( Path, graph_search(e, Path), Paths ). ... Paths = [ [e, d, a, b, c], [e, b, c] ] Yes ?- Prof. Dr. Dietmar Seipel 456 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Man kann mit P ROLOG (im Gegensatz zur “reinen” Logikprogrammierung) auch prozedural mit Seiteneffekten und mit globalen Variablen (realisiert mit Hilfe von assert und retract) programmieren. Es gibt umfangreiche Programmbibliotheken und Erweiterungen: Datenstrukturen und Algorithmen, Kombinatorik, Datenbank– und Web–Programmierung, GUI–Programmierung, etc. Prof. Dr. Dietmar Seipel 457 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Anwendungsbereiche: regelbasierte Diagnosesysteme (Medizin, Technik) symbolische Informationsverarbeitung, Parsing Datenbanken mit komplexen Strukturen (Hierarchien, X ML) Datenintegration Semantic Web Default Reasoning kombinatorische Suchprobleme (Graphenprobleme, Spiele wie Sudoku oder Minesweeper, etc.) Prof. Dr. Dietmar Seipel 458 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Quicksort in P ROLOG /* quicksort(List, Sorted_List) <*/ quicksort([], []) :!. quicksort([X|Xs], Ys) :quicksort_divide(X, Xs, Xs1, Xs2), quicksort(Xs1, Ys1), quicksort(Xs2, Ys2), append(Ys1, [X|Ys2], Ys). Prof. Dr. Dietmar Seipel 459 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 quicksort_divide(X, Xs, Xs1, Xs2) :findall( X1, ( member(X1, Xs), X1 < X ), Xs1 ), findall( X2, ( member(X2, Xs), X2 > X ), Xs2 ). append([], Ys, Ys). append([X|Xs], Ys, [X|Zs]) :append(Xs, Ys, Zs). Prof. Dr. Dietmar Seipel 460 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Mergesort in P ROLOG /* mergesort(List, Sorted_List) <*/ mergesort(Xs, Xs) :length(Xs, N), N =< 1, !. mergesort(Xs, Ys) :middle_split(Xs, Xs1, Xs2), mergesort(Xs1, Ys1), mergesort(Xs2, Ys2), mergesort_merge(Ys1, Ys2, Ys). Prof. Dr. Dietmar Seipel 461 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 mergesort_merge([], Xs, Xs) :!. mergesort_merge(Xs, [], Xs) :!. mergesort_merge([X1|Xs1], [X2|Xs2], [X|Xs]) :( X1 < X2, X = X1, mergesort_merge(Xs1, [X2|Xs2], Xs) ; X = X2, mergesort_merge([X1|Xs1], Xs2, Xs) ). Prof. Dr. Dietmar Seipel 462 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Binäre Suchbäume in P ROLOG /* search_in_binary_tree(Key, Tree) <*/ search_in_binary_tree(Key, Tree) :binary_tree_parse(Tree, Key, _, _). search_in_binary_tree(Key, Tree) :binary_tree_parse(Tree, Root, Left, Right), ( ( Key < Root, !, Tree = Left ) ; ( Key > Root, Tree = Right ) ), search_in_binary_tree(Key, Tree). Prof. Dr. Dietmar Seipel 463 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Kapselung des Zugriffs: /* binary_tree_empty(Tree) <*/ binary_tree_empty(Tree) :( dislog_variable_get(binary_tree_mode, xml), Tree = node:[]:[] ; Tree = [] ). Prof. Dr. Dietmar Seipel 464 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 /* binary_tree_parse(Tree, Key, Lson, Rson) <*/ binary_tree_parse(Tree, Key, Empty, Empty) :binary_tree_empty(Empty), ( dislog_variable_get(binary_tree_mode, xml), Tree = node:[key:Key]:[] ; Tree = [Key] ), !. binary_tree_parse(Tree, Key, Lson, Rson) :( dislog_variable_get(binary_tree_mode, xml), Tree = node:[key:Key]:[Lson, Rson] ; Tree = [Key, Lson, Rson] ). Prof. Dr. Dietmar Seipel 465 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Suchbaum in X ML–Darstellung: <node key="8"> <node key="4"> <node key="2"> <node key="1"/> <node key="3"/> </node> <node key="6"> <node key="5"/> <node key="7"/> </node> </node> <node key="12"> <node key="10"> <node key="9"/> <node key="11"/> </node> <node key="13"/> </node> </node> Prof. Dr. Dietmar Seipel 466 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 Suchbaum X ML–Darstellung: <node key="2"> <node key="1"/> <node key="3"/> </node> P ROLOG–Darstellung: node:[key:2]:[ node:[key:1], node:[key:3] ] alternative P ROLOG–Darstellung: [ 2, [1], [3] ] Prof. Dr. Dietmar Seipel 467 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 /* insert_into_binary_tree(Key, Tree, New_Tree) <*/ insert_into_binary_tree(Key, Tree, New_Tree) :binary_tree_parse(Tree, Root, Left, Right), ( ( Key = Root, % Key is already in Tree New_Tree = Tree ) ; ( Key < Root, insert_into_binary_tree(Key, Left, L), binary_tree_parse(New_Tree, Root, L, Right) ) ; ( Key > Root, insert_into_binary_tree(Key, Right, R), binary_tree_parse(New_Tree, Root, Left, R) ) ). insert_into_binary_tree(Key, Tree, New_Tree) :binary_tree_empty(Tree), binary_tree_parse(New_Tree, Key, Tree, Tree). Prof. Dr. Dietmar Seipel 468 Praktische Informatik I – Algorithmen und Datenstrukturen Wintersemester 2006/07 /* keys_to_binary_tree(Keys, Tree) <*/ keys_to_binary_tree(Keys, Tree) :binary_tree_empty(Empty), insert_into_binary_tree_(Keys, Empty, Tree). /* insert_into_binary_tree_(Keys, Tree, New_Tree) <*/ insert_into_binary_tree_([], Tree, Tree). insert_into_binary_tree_([Key|Keys], Tree, New_Tree) :insert_into_binary_tree(Key, Tree, Tree_2), insert_into_binary_tree_(Keys, Tree_2, New_Tree). Prof. Dr. Dietmar Seipel 469