Einführung in die Informatik: Digitale Informationsverarbeitung WS

Werbung
Kapitel 6. Suchverfahren
Wir betrachten nun Listen, in denen die einzelnen Elemente (Knoten)
sowohl einen Inhalt als auch einen Schlüssel besitzen. In C kann jetzt
der Strukturtyp Knoten z. B. in folgender Weise definiert sein:
typedef struct Knoten
{
int
key;
char
*Inhlt; /*Inhlt ist Zeiger auf eine Zeichenkette. */
struct Knoten *next;
}ListElmt;
Definieren wir nun eine Variable des Typs ListElmt
ListElmt *elmnt;
Dann beinhaltet elmnt->key den Schlüsselwert (hier eine integer Größe).
Für ein Wörterbuch würde man char key; statt int key; vereinbaren, so daß
elmnt->key (Schlüsselwert) dann ein beliebige Zeichenkette sein kann.
R. Der
1
Algorithmen und Datenstrukturen (Magister)
Sequentielle Suche
Suche nach Element mit Schlüsselwert K
Falls nicht bekannt, ob die Elemente der Liste nach ihren
Schlüsselwerten sortiert sind, besteht nur die Möglichkeit, die
Liste sequentiell zu durchlaufen und elementeweise zu überprüfen
(sequentielle Suche)
Kosten:
•Erfolglose Suche erfordert n Schleifendurchläufe
• erfolgreiche Suche verlangt im ungünstigsten Fall
n -1 Schleifendurchläufe ( und n Schlüsselvergleiche)
• mittlere Anzahl von Schleifendurchläufen bei erfolgreicher Suche:
1 n-1
n-1
Cavg (n) =  i =
n i=0
2
R. Der
2
Algorithmen und Datenstrukturen (Magister)
Binäre Suche
Auf sortierten Listen können Suchvorgänge effizienter
durchgeführt werden
Sequentielle Suche auf sortierten Listen bringt nur geringe
Verbesserungen (für erfolglose Suche durchschnittlich N/2
Vergleiche).
Binärsuche wesentlich effizienter durch den Einsatz der
Divide-and-conquer-Strategie.
Suche nach Schlüssel K in Liste mit aufsteigend sortierten
Schlüsseln:
R. Der
3
Algorithmen und Datenstrukturen (Magister)
1. Falls Liste leer ist, endet die Suche erfolglos.
Sonst: Betrachte Element der Liste an mittlerer Position m.
2. Falls K = Schlüsselwert dieses Elementes, dann ist das
gesuchte Element gefunden .
3. Falls K < Schlüsselwert, dann durchsuche die linke
Teilliste von Position 1 bis m-1 nach demselben
Verfahren.
4. Sonst (K > Schlüsselwert) durchsuche die rechte Teilliste
von Position m + 1 bis Listenende nach demselben
Verfahren.
R. Der
4
Algorithmen und Datenstrukturen (Magister)
Binäre Suche (2)
Iterative Lösung
int binsearch(int v)
{
int l=1; int r= N; int x;
while (r>=1)
{
x = (l+r)/2;
if (v < a[x].key) r = x-1; else l = x+1;
if (v == a[x].key) return a[x].data;
}
return -1
}
R. Der
5
Algorithmen und Datenstrukturen (Magister)
Kosten
Cmin ( n ) = 1
Cmax ( n ) = [ log2 (n+1)]
Cavg ( n )  log2 (n+1) -1, für große n
R. Der
6
Algorithmen und Datenstrukturen (Magister)
Fibonacci-Suche
Ähnlich der Binärsuche, jedoch wird Suchbereich
entsprechend der Folge der Fibonacci-Zahlen geteilt.
Definition der Fibonacci-Zahlen
F0 = 0
F1 = 1
Fk = F k-1 + F k-2 für k >= 2.
Teilung einer Liste mit n = Fk-1 sortierten Elementen:
1
i
n
F k-2 -1
F k-1 -1
F k- 1
R. Der
7
Algorithmen und Datenstrukturen (Magister)
- Element an der Position i = F k-2 wird mit dem Schlüssel K
verglichen
- Wird Gleichheit festgestellt, endet die Suche erfolgreich.
- Ist K größer, wird der rechte Bereich mit F k-1 -1
Elementen, ansonsten der linke Bereich mit F k-2 -1
Elementen auf dieselbe Weise durchsucht.
Kosten
- für n = F k -1 sind im schlechtesten Fall k-2 Suchschritte
notwendig, d. h. O ( k ) Schlüsselvergleiche
- Da gilt F k  c + 1.618 k , folgt
C max ( n ) = O ( log 1.618 ( n+1 ) ) = O ( log2 n) .
R. Der
8
Algorithmen und Datenstrukturen (Magister)
Sprungsuche
Prinzip
- Zunächst wird der sortierte Datenbestand in
Sprüngen überquert, um den Abschnitt zu
lokalisieren, der ggf. den gesuchten Schlüssel enthält,
- danach wird der Schlüssel im gefundenen Abschnitt
nach irgendeinem Verfahren gesucht.
...
...
L
1
R. Der
...
m
...
2m
...
3m
9
n
Algorithmen und Datenstrukturen (Magister)
Einfache Sprungsuche
- konstante Sprünge zu Positionen m, 2 m, 3 m, ...
- Sobald K <= Schlüsselwert[i] mit i = j * m (j = 1, 2, ...),
wobei a[i].key der Wert des inspizierten Schlüssels ist
wird im Abschnitt
von (j-1)m+1 bis j*m
sequentiell nach dem Suchschlüssel K gesucht.
Mittlere Suchkosten
ein Sprung koste a ; ein sequentieller Vergleich b Einheiten
Cavg (n) = (a*n/m + b*(m-1))/2
R. Der
10
Algorithmen und Datenstrukturen (Magister)
Optimale Sprungweite
m=V(a/b)n
bzw. m = V n
falls a = b
 C avg ( n ) = a V n - a / 2
 Komplexität
O (V n )
R. Der
11
Algorithmen und Datenstrukturen (Magister)
Exponentielle Suche
Anwendung wenn Länge des sortierten Suchbereichs
zunächst unbekannt bzw. sehr groß ist.
Vorgehensweise
- für Suchschlüssel K wird zunächst obere Grenze für den
zu durchsuchenden Abschnitt bestimmt
i = 1;
while (K > Schlüsselwert[i])
i *= 2;
- Für i > 1 gilt für den auf diese Weise bestimmten
Suchabschnitt
Schlüsselwert[i DIV 2] < K <= Schlüsselwert[i]
- Suche innerhalb des Abschnitts mit irgendeinem Verfahren
R. Der
12
Algorithmen und Datenstrukturen (Magister)
Sind in der sortierten Liste nur positive, ganzzahlige
Schlüssel ohne Duplikate gespeichert,
wachsen Schlüsselwerte mindestens so stark wie die
Indizes der Elemente.
- i wird höchstens log2 K mal verdoppelt
- Bestimmung des gesuchten Intervalls erfordert maximal
log2 K Schlüsselvergleiche
- Suche innerhalb des Abschnitts (z. B. mit Binärsuche )
erfordert auch höchstens log2 K Schlüsselvergleiche
Gesamtaufwand O ( log2 K )
R. Der
13
Algorithmen und Datenstrukturen (Magister)
Interpolationssuche
Schnellere Lokalisierung des Suchbereichs indem
Schlüsselwerte selbst betrachtet werden, um „Abstand“ zum
Schlüssel K abzuschätzen
nächste Suchposition pos wird aus den Werten ug und og
der Unter- und Obergrenze des aktuellen Suchbereichs wie
folgt berechnet:
K - Schlüsselwert[ug]
pos = ug +
R. Der
* (og - ug)
Schlüsselwert[og]- Schlüsselwert[ug]
14
Algorithmen und Datenstrukturen (Magister)
Sinnvoll, wenn der Schlüsselwert im betreffenden Bereich
einigermaßen gleichverteilt ist
erfordert dann im Mittel lediglich
log2 log2n + 1
Schlüsselvergleiche
Im schlechtesten Fall
(stark ungleichmäßige Werteverteilung) entsteht jedoch
linearer Suchaufwand ( O ( n ) )
R. Der
15
Algorithmen und Datenstrukturen (Magister)
Anmerkungen zur Implementierung in C
Strukturierte Typen in C
Struktur ist eine Ansammlung von mehreren Variablen unter einem gemeinsamen Namen.
Syntax für einen Strukturtyp:
struct Bezeichner {Komponenten}.
Komponenten (im einfachsten Falle):
Typ Komponentenname;
Danach können gleich noch Variable des eigeführten Typ vereinbart werden.
Beispiel: Implementierung einer Liste durch ein Array:
struct List
{
double A[10];
int length;
} l1,l2; l1 und l2 sind jetzt Variable des vereinbarten Typs.
Ansprache über Variablenname.Komponentenname. Beispiele:
l1.A[3]=3.2;
l1.length = 10;
R. Der
16
Algorithmen und Datenstrukturen (Magister)
Rekursive Definition eines Strukturtyps
Rekursive Definition nur über Zeiger möglich.
struct List
{
double
int
struct List
} l1,l2;
A[10];
length;
*liste;
liste ist damit einZeiger auf eine Struktur vom Typ List.
R. Der
17
Algorithmen und Datenstrukturen (Magister)
Verwendung von typedef
Mit typedef kann ein neuer Name nach Bedarf für einen Datentyp eingeführt
werden.
Syntax:
typedef typ declarator
Im einfachsten Falle ist der declarator einfach ein Bezeichner:
typedef typ bezeichner
Beispiel für letzteren Fall:
typedef struct List
{
double A[10];
int length;
} Meine_Liste;
Dabei ist struct List {double A[10]; int length;} der typ und Meine_Liste ist jetzt der
bezeichner für den durch die struct Deklaration eingeführten Datentyp List. Man kann dann
Variablen dieses Typs als
Meine_Liste L1;
deklarieren. Speicher anfordern (Objekt erzeugen) mit
L1 = (Meine_Liste *)malloc (sizeof L1);
Nun geht z. B.
L1->length = 10;
R. Der
18
Algorithmen und Datenstrukturen (Magister)
Herunterladen