11. Rekursive Datenstrukturen

Werbung
11. Rekursive Datenstrukturen
•
Lineare Listen
– Liste = leer | Element Liste
1. Die leere Liste ist eine Liste
2. Wird einer Liste ein Element angehängt, so erhalten wir
wiederum eine Liste
•
Rekursive Implementierung von Listen
– Die Klasse Node enthält „sich selbst“: Rekursion
– Die Liste wird (über node.next) so lange „reduziert“,
bis wir zur trivialen (leeren) Liste kommen
– Die Suche in einer Schleife wird durch den
rekursiven Abstieg ersetzt
László Böszörményi
ESOP
Rekursive Datenstrukturen - 1
Sortierte Listen
•
Auf den Elementen ist eine Ordnung definiert
–
•
•
Ordnungsfunktion f ist für jedes Elementpaar (e1, e2) definiert:
•
f (e1) > f(e2), wenn e1 größer als e2 ist
•
f (e1) < f(e2), wenn e1 kleiner als e2 ist
•
f (e1) == f(e2), wenn e1 und e2 gleich sind
Wir halten die Elemente nach dieser Funktion geordnet
in der Liste
–
Beim Einfügen müssen wir die richtige Stelle finden
–
Beim Suchen können wir die Ordnung ausnutzen
Die Ordnung basiert oft auf einem Alleinmerkmal (einem
Schlüsselwert), wie Matrikelnummer oder Name
László Böszörményi
ESOP
Rekursive Datenstrukturen - 2
Operationen auf sortierten Listen
Einfügen
(key = 3)
previous
actual
1
4
head
key
next
-3
9
null
x
Löschen
(key = 4)
3
previous.next = x;
x.next = actual;
previous
actual
1
4
head
key
next
-3
9
null
previous.next = actual.next;
László Böszörményi
ESOP
Rekursive Datenstrukturen - 3
Interface List
public interface List {
// 03.11.2000. LB
public boolean search (int key);
// Retourniert true, wenn Knoten mit Schlüssel = key gefunden, sonst false
public void insert (int key);
// Fügt einen Knoten mit Schlüssel=key ein
// Wenn ein Element (Schlüssel=key) schon existiert: Neue zuerst einfügen
public boolean remove (int key);
/* Retourniert false, wenn Knoten mit Schlüssel = key nicht gefunden,
sonst entfernt den Knoten aus der Liste und gibt true zurück */
public String toString ();
// Wandelt die Liste in eine Zeichenkette um
} // List
László Böszörményi
ESOP
Rekursive Datenstrukturen - 4
Benutzerklasse sortierter Listen
public class ListUser {
// 03.11.2000. LB
static void useList(List list) {
for (int i = 9; i > 0; i -= 2) list.insert(i); // Elemente Einfügen
Out.println(list);
(13579)
for (int i = -2; i < 14; i+= 2) list.insert(i);
Out.println(list);
( -2 0 1 2 3 4 5 6 7 8 9 10 12 )
for (int i = -5; i < 16; i+= 4) {
if (list.remove (i)) Out.println(i + " entfernt ");
else Out.println(i+ " nicht enthalten");
-5 nicht enthalten
} // for
-1 nicht enthalten
3 entfernt
Out.println(list);
7 entfernt
} // useList
( -2 0 1 2 4 5 6 8 9 10 12 )
11 nicht enthalten
static void main(String [] args) {
15 nicht enthalten
useList(new NonRekList ());
} // main
} // ListUser
László Böszörményi
ESOP
Rekursive Datenstrukturen - 5
Nicht-rekursive Implementierung (1)
public class NonRekList implements List { // 03.11.2000. LB
private class Node {
// 03.11.2000. LB
private int
info;
private Node
next;
private Node (int info, Node next) { this.info = info; this.next = next;}
} // Node
private Node head = null;
public boolean search (int key) {
// Gibt true zurück, wenn gefunden
Node actual = head;
// „Lazy“ Auswertung ausnützen:
while (actual != null && key > actual.info) actual = actual.next;
return (actual != null) && (actual.info == key); // true, wenn gefunden
} // search
László Böszörményi
ESOP
Rekursive Datenstrukturen - 6
Nicht-rekursive Implementierung (2)
public String toString () {
// Liste in Klammern als String zurück
Node actual = head;
String listString = "";
while (actual != null ) {
listString += actual.info + " ";
actual = actual.next;
} // while
return "( " + listString + ")";
} // toString
László Böszörményi
ESOP
Rekursive Datenstrukturen - 7
Nicht-rekursive Implementierung (3)
public void insert (int key) { // Fügt Knoten mit Schlüssel=key ein
if ( (head == null) || (key <= head.info) )
// Bei Gleichheit wird das
head = new Node(key, head);
// Neue zuerst eingefügt
else {
// Richtige Stelle suchen
Node previous = head, actual = head.next;
while (actual != null && key > actual.info) {
previous = actual;
actual = actual.next;
} // while
previous.next = new Node(key, actual);
} // if
} // insert
László Böszörményi
ESOP
Rekursive Datenstrukturen - 8
Nicht-rekursive Implementierung (4)
public boolean remove (int key) {
// Entfernt Knoten mit key und retourniert true, wenn gefunden, sonst false
if (head == null) return false;
else if (head.info == key) {
head = head.next; return true;
// Element am Kopf entfernt
} // if (head.info == key)
else { Node previous = head, actual = head.next; // Suchen
while (actual != null && key > actual.info) {
previous = actual; actual = actual.next;
} // while
if (actual == null) || (key != actual.info) return false; // Nicht gef.
else { previous.next = actual.next; // Entfernt actual aus der Liste
return true; }
// gefunden: true zurück
} // if (head == null)
} // remove
} // NonRekList
László Böszörményi
ESOP
Rekursive Datenstrukturen - 9
Rekursive Implementierung von Listen (1)
public class RekList implements List {
// 03.11.2000. LB
private class Node {
private int
info;
private Node
next;
private Node (int info, Node next) { this.info = info; this.next = next;}
} // Node
private Node head = null;
protected Node search (int key, Node node) { // Nur in Subklassen bekannt
if (node == null)
return null;
else if (node.info == key) return node;
else
return search(key, node.next);
} // search
public boolean search (int key) {
Node n = search(key, head);
if (n == null) return false; else return true;
} // search
László Böszörményi
ESOP
Rekursive Datenstrukturen - 10
Rekursive Implementierung von Listen (2)
protected String toString (Node node, String listString) {
if (node == null) {
return listString;
} else {
return toString (node.next, listString + node.info + " ");
} // if
} // toString
public String toString () {
return "( " + toString (head, "") + ")";
} // toString
László Böszörményi
ESOP
Rekursive Datenstrukturen - 11
Rekursive Implementierung von Listen (3)
protected Node insert(int key, Node node) {
if ( (node == null) || (key <= node.info) )
return new Node(key, node);
else {
node.next = insert(key, node.next);
return node;
} // if
} // insert
public void insert(int key) {
head = insert(key, head);
} // insert
László Böszörményi
ESOP
Rekursive Datenstrukturen - 12
Rekursive Implementierung von Listen (4)
private class Found { boolean found = false; } // Speichert nur einen Boolean
protected Node remove (int key, Node node, Found f) {
if (node == null) { return null;
// Ende der Liste erreicht
} else if (node.info == key) {
// Gefunden
f.found = true;
return node.next; } // if (node == key)
else {
// Weitersuchen
node.next = remove (key, node.next, f);
return node;
} // else
} // remove
public boolean remove (int key) {
Found f = new Found();
head = remove (key, head, f);
return f.found;
} // remove}
// RekList
László Böszörményi
ESOP
Rekursive Datenstrukturen - 13
Lineares Suchen in einem Array
•
Wir müssen das Array durchsuchen, solange wir entweder
–
–
•
Das gesuchte Element gefunden haben oder
Das Ende des Arrays erreicht haben
Implementierung (iterativ bzw. rekursiv)
–
Suchfunktion retourniert den Index des gefundenen Elementen bzw. -1
static int LinearSearch (int [] arr, int key) { // Iteratives lineares Suchen
int i = 0;
while ( (i < arr.length) && (arr[i] != key) ) i++;
if (i == arr.length) return -1;
// nicht gefunden: gibt -1 zurück
else return i;
// gefunden an Stelle i
} // LinearSearch
static int RekSearch (int [] arr, int key, int start) {// Rekursives lineares Suchen
if (start == arr.length) return -1;
// nicht gefunden : -1 zurück
else if (arr[start] == key) return start;
// gefunden an Stelle start
else return RekSearch(arr, key, start + 1); // weitersuchen ab start+1
} // RekSearch
László Böszörményi
ESOP
Rekursive Datenstrukturen - 14
Bäume („Schnupperkurs“ – Rest in SW2)
•
•
Ein Baum ist ein gerichteter Graph, in dem jeder Knoten
außer der Wurzel genau einen Vorgänger („Vater“) hat
Eine Baumstruktur mit Grundtyp T ist entweder
1. Die leere Struktur oder
2. Ein Knoten vom Typ T mit einer endlichen Anzahl verknüpfter
Baumstrukturen mit Grundtyp T (die sog. Teilbäume)
Wurzel
Höhe = 3
•
•
•
Bäume sind immer azyklisch
Pfadlänge: Die Anzahl der Kanten zwischen zwei Knoten
Höhe: Maximale Pfadlänge aus der Wurzel heraus
–
–
Höhe des leeren Baumes = 0
Höhe eines Baumes der nur aus Wurzel besteht = 1
László Böszörményi
ESOP
Rekursive Datenstrukturen - 15
Binärbäume
•
•
•
In einem Binärbaum hat jeder Knoten höchstens 2
Nachfolger („Kinder“)
Binärbaum = leer | Knoten Binärbaum Binärbaum
Ein Binärbaum ist entweder leer oder besteht aus einem
Knoten (Wurzelknoten) und 2 Teilbäume
Wurzel
Linker „Sohn“
László Böszörményi
Rechter „Sohn“
ESOP
Höhe = 5
Rekursive Datenstrukturen - 16
Binäre Suchbäume
•
In einem binären Suchbaum (geordneten Binärbaum) gilt für
jeden Knoten, dass
–
–
Alle Schlüsselwerte im linken Teilbaum kleiner
Alle Schlüsselwerte im rechten Teilbaum größer oder gleich
sind, wie der Schlüsselwert im Knoten selbst
(Ob wir gleiche Knoten rechts oder links einordnen ist Definitionssache)
50
20
10
5
70
35
10
László Böszörményi
60
40
90
85
ESOP
95
Rekursive Datenstrukturen - 17
Impliziter Suchbaum – Binäres Suchen
•
Wir suchen in einem geordneten Array
–
–
–
Wir schauen rekursiv, ob ein Element in der linken oder rechten
Hälfte des Arrays enthalten ist
Ist die Größe des Arrays n, kostet das höchstens log2N Schritte
(N ist die kleinste Zweierpotenz > n)
Es wird implizit ein Suchbaum, der Höhe log2N aufgebaut
30, 71, 92, 233, 284, 315, 326, 357, 478, 559, 6110, 7211
Suche von 28
315
92
30
478
233
71
László Böszörményi
6110
326
284
357
ESOP
Höhe = 4
(log216)
559
7211
Rekursive Datenstrukturen - 18
Implementierung vom Binären Suchen
static int BinSearch (int [] arr, int left, int right, int key) {
/* Gibt den Index des gefundenen Elementen bzw. -1 (falls nicht gefunden) zurück */
int middle = left + (right - left) / 2;
// Wurzel des nächsten Teilbaums
Out.println("arr[" + middle + "] = " + arr[middle]);
// Testausgabe
if (key == arr[middle]) return middle; // key == arr[middle]: gefunden
else if (key < arr[middle])
// key < arr[middle]: suche links
if (left < middle) return BinSearch(arr, left, middle - 1, key); arr[5] = 31
31 gefunden
else return -1;
// nicht gefunden
(in 1 Schritt)
else
// key >= arr[middle]: suche rechts
if (middle < right) return BinSearch(arr, middle + 1, right, key);
else return -1;
// nicht gefunden
arr[5] = 31
arr[5] = 31
} // BinSearch
int [] q = {3, 7, 9, 23, 28, 31, 32, 35, 47, 55, 61, 72};
int x = BinSearch (q, 0, q.length - 1, 28);
László Böszörményi
ESOP
arr[2] = 9
arr[3] = 23
arr[4] = 28
28 gefunden
arr[8] = 47
arr[10] = 61
arr[11] = 72
99 nicht gefunden
Rekursive Datenstrukturen - 19
Herunterladen