64
1 Søgning I

Søgning I

  • Upload
    becca

  • View
    28

  • Download
    2

Embed Size (px)

DESCRIPTION

Søgning I. Plan. Sekventiel søgning Binær søgning Binære søgetræer ------------------------------------------- Balancerede binære søgetræer - 2-3-4-træer - rød - sort -træer. - PowerPoint PPT Presentation

Citation preview

Page 1: Søgning I

1

Søgning I

Page 2: Søgning I

2

Plan

• Sekventiel søgning

• Binær søgning

• Binære søgetræer

-------------------------------------------

• Balancerede binære søgetræer

- 2-3-4-træer

- rød-sort-træer

Page 3: Søgning I

3

Søgning er det problem at afgøre, om en mængde af objekter indeholder et objekt, der opfylder visse specificerede krav, og i givet fald finde det.

Søgning

Søgning er genfinding af lagret information. Informationen har form af poster, der hver har en nøgle. Målet med en søgning er at finde den eller de poster, der har en nøgle, der matcher en given søgenøgle.

Søgning er den mest tidsforbrugende aktivitet i mange programmer. At erstatte en dårlig metode med en god fører derfor ofte til en væsentlig effektivitetsforøgelse.

Page 4: Søgning I

4

Klassifikation af søgemetoder

• intern/ekstern søgning

• statisk/dynamisk søgning

• søgning baseret på nøglesammen-ligninger/digitale egenskaber ved nøglerne

• søgning baseret på de aktuelle nøgler/ transformerede nøgler

Page 5: Søgning I

5

Mængden af poster med tilhørende operationer beskrives ved en abstrakt datatype, Dictionary.

Ordbog (symboltabel)en abstrakt datatype til søgning

Metoden insert indsætter en ny post med nøgle v og tilknyttet information, info, i ordbogen.

Metoden search leder efter en post, der har v som nøgle. Hvis søgningen lykkes, returneres postens tilknyttede information. Ellers returneres en værdi, der indikerer “ingen information” (infoNil).

class Dictionary { void insert(keyType v, infoType info); infoType search(keyType v); }

Page 6: Søgning I

6

Andre operationer

• Initialisering (ved hjælp af en eller flere konstruktører)

• Genfinding af alle poster med en given nøgle: Enumeration searchAll(keyType v)

• Sletning af poster med en given nøgle: void remove(keyType v)

• Sortering af en ordbogs poster: Vector sort()

• Sammenlægning af to ordbøger: Dictionary join(Dictionary other)

Page 7: Søgning I

7

( keyType = int, infoType = String, infoNil = null )

class Node { int key; String info; }

Repræsentation ved usorteret array af poster

class Dictionary { private Node a[]; private int N;

Dictionary(int max) { a = new Node[max+1]; N = 0; for (int i = 0; i <= max; i++) a[i] = new Node(); }

void insert(int v, String info) { a[++N].key = v; a[N].info = info; }

String search(int v) { ...}}

Page 8: Søgning I

8

String search(int v) {a[0].key = v; a[0].info =

null; int i = N+1;while (v != a[--i].key) ;return a[i].info;

}

Sekventiel (lineær) søgning

Sekventiel søgning, implementeret med usorteret array, bruger altid N+1 sammenligninger for en mislykket søgning og gennemsnitligt (N+1)/2 sammenligninger for en succesfuld søgning.

Kompleksitet af søgning er O(N). Kompleksitet af indsættelse er O(1).

Page 9: Søgning I

9

class Node { int key; String info; Node next;

Node(int k, String i, Node n) { key = k; info = i; next = n; } }

Repræsentation ved usorteret liste af poster

class Dictionary { private Node a[], head, z; private int N;

Dictionary(int max) { z = new Node(Integer.MAX_VALUE, null, null); head = new Node(0, null, z); } void insert(int v, String info) { ... } String search(int v) { ... } }

Page 10: Søgning I

10

void insert(int v, String info) {head.next =

new Node(v, info, head.next); }

Indsættelse og søgning i usorteret liste

Kompleksiteten er den samme som for et usorteret array.

String search(int v) { Node t = head; while ((t = t.next) != z) if (t.key == v) return t.info; return null;}

Page 11: Søgning I

11

Indsættelse og søgning isorteret liste

void insert(int v, String info) {Node t = head;while (v > t.next.key) t = t.next;t.next = new Node(v, info, t.next); }

Sekventiel søgning, implementeret med sorteret liste, bruger gennemsnitligt cirka N/2 sammenlig-ninger for både succesfuld og mislykket søgning.

Kompleksiteten af såvel indsættelse som søgning er O(N).

String search(int v) { Node t = head; while (v > t.next.key) t = t.next; return v == t.key ? t.info : null; }

Page 12: Søgning I

12

void insert(int v, String info) { int i = ++N;while (a[i-1].key > v) { a[i] = a[i-1]; i--; }

a[i] = new Node(v, info); }

Repræsentation vedsorteret array

Kompleksiteten af insert er O(N).

Metoden search kan implementeres som for usorteret array (med kompleksitet O(N)). Mere effektivt er det dog at benytte binær søgning.

Page 13: Søgning I

13

Binær søgning

Metode:

Opdel arrayet i to (næsten) lige store dele.

Afgør i hvilken af de to dele, nøglen skal findes.

Fortsæt søgningen i denne del på samme måde.

Eksempel: Søgning efter M.

A A A C E E E G H I L M N P R S X I L M N P R S X I L M M

Page 14: Søgning I

14

Rekursiv udgave:

String search(int v, int l, int r) {

if (l <= r) {

int x = (l+r)/2;

if (v == a[x].key) return a[x].info;

if (v < a[x].key) return search(v, l, x-

1);

return search(v, x+1, r);}

return null; }

Implementation afbinær søgning

Kald: info = search(v, 1, N);

Page 15: Søgning I

15

Implementation afbinær søgning

Iterativ udgave:

String search(int v) {

int l = 1, r = N;

while (l <= r) {

int x = (l+r)/2;

if (v == a[x].key) return a[x].info;

if (v < a[x].key) r = x-1;

else l = x+1; }

return null; }

Page 16: Søgning I

16

Alternativ implementation afbinær søgning

String search(int v) {

int l = 1, r = N;

while (l < r) {

int x = (l+r+1)/2;

if (v < a[x].key) r = x-1;

else l = x; }

return v == a[l].key ?a[l].info : null; }

Binær søgning blev første gang beskrevet i 1946.Den første fejlfri udgave blev publiceret i 1960.

I 1986 fandt Bentley, at 90% af alle ”computer professionals” ikke kunne skrive en fejlfri udgave på to timer.

Page 17: Søgning I

17

Kompleksiteten af binær søgning

Binær søgning bruger aldrig mere end log2N + 1 sammenligninger - for såvel succesfuld som mislykket søgning.

Binær søgning kan beskrives ved et sammenlignings-træ:

H

C

A

A A E E I M P S

E L R

N

G X

Page 18: Søgning I

18

Antal sammenligninger: Binær søgning:

CB(N) = 1 + CB(N/2) for N ≥ 2, CB(1) = 1

som har løsningen CB(N) = log2(N) + 1, for N ≥ 1.

Ternær søgning: CT(N) = 1/3*1 + 2/3*2 + CT(N/3) for N ≥ 2, CT(1) = 1

som har løsningen CT(N) = 5/3*log3(N) + 1 for N ≥ 1.

Idet 5/3*log3(N) + 1 = 5/3*log2(N)/log2(3) + 1 og 5/3 > log2(3), får vi, at CT(N) > CB(N) for N ≥ 1.

Svaret er altså: nej.

Er ternær søgning bedre end binær søgning?

Page 19: Søgning I

19

Interpolationssøgning

Indeksintervallet opdeles efter et gæt på nøglens placering. Ved lineær interpolation sammenholdes søgenøglen med nøglerne i de to intervalendepunkter.

I binær søgning erstattes x = (l + r)/2 med

x = l + (v-a[l])*(r-l)/(a[r]-a[l]).

a[r]

a[l]

v

l x r

Page 20: Søgning I

20

Kompleksiteten af interpolationssøgning

Interpolationssøgning bruger cirka log2log2N + 1 sammenligninger for både succesfuld og mislykket søgning på tilfældige filer.

Færre end 5 forsøg i praksis (225 ≈ 4*1010).

Svagheder: (1) filerne er ikke “tilfældige”, (2) beregningerne af x kan koste mere end de sparede sammenligninger.

Page 21: Søgning I

21

Binære søgetræer

Ved et binært søgetræ forstås et binært træ bestående af poster med nøgler, hvor der for enhver knude gælder, at alle poster i venstre undertræ er mindre end eller lig med knudens nøgle, mens alle knuder i højre undertræ er større end knudens nøgle.

H

C

A

A I M P S

L R

N

X

head

Page 22: Søgning I

22

Class Dictionaryimplementeret ved binære søgetræer

class Node { int key; String info; Node l, r;

Node(int k, String i, Node ll, Node rr) { key = k; info = i; l = ll; r = rr; }}

Page 23: Søgning I

23

class Dictionary { private Node head, z; private int N;

Dictionary() { z = new Node(0 ,null, null, null); head = new Node(Integer.MIN_VALUE,

null, null, z); }

void insert(int v, String info) { ... } String search(int v) { ... } void remove(int v) { ... }}

Page 24: Søgning I

24

String search(int v) { Node x = head.r; z.key = v; while (v != x.key) x = v < x.key ? x.l : x.r; return x.info; }

Søgning i binære søgetræer

X

M

D

A

Antallet af sammenligninger afhænger af søgetræets udseende. I bedste fald, nemlig når træet er fuldt, udføres cirka log2N sammenligninger. I værste fald, nemlig når træet er en lineær liste, udføres N+1 sammenligninger ved mislykket søgning.

Page 25: Søgning I

25

Der foretages en mislykket søgning, og den nye knude indsættes på den eksterne knudes plads. For at knuden kan indsættes i træet, bestemmes dens farknude, p.

void insert(int v, String info) {

Node p = head, x = head.r;

while (x != z) {

p = x;

x = v < x.key ? x.l : x.r;}

x = new Node(v, info, z, z);

if (v < p.key) p.l = x; else p.r = x; }

Indsættelse i binære søgetræer

Page 26: Søgning I

26

void insert(int v, String info) { insertR(head, v, info); }

Node insertR(Node n, int v, String info) { if (n == z)

return new Node(v, info, z, z); if (v < n.key)

n.l = insertR(n.l, v, info); else

n.r = insertR(n.r, v, info); return n;

}

Rekursiv udgave af insert

Page 27: Søgning I

27

String searchR(Node n, int v) {

if (n == z) return null;

if (v == n.key) return n.info;

if (v < n.key) return searchR(n.l, v);

return searchR(n.r, v); }

Rekursiv udgave af search

public String search(int v) { return searchR(head.r, v); }

Page 28: Søgning I

28

Kompleksiteten af søgning og indsættelse

En søgning eller en indsættelse i et binært søgetræ kræver i gennemsnit cirka 2 lnN sammenligninger i et træ, der er opbygget ud fra N tilfældige nøgler.

2 lnN ≈ 1.39 log2N

Eksempel: N = 106 2 ln N ≈ 27.6log2N ≈ 19.9

Page 29: Søgning I

29

Sletning i binære søgetræer

Sletning af roden i et binært søgetræ (H):

Erstat roden med den næste højere post (I ).

R

H

C

A

A I M P S

L

N

XK

R

I

C

A

A M P S

L

N

X

K

Page 30: Søgning I

30

c.l = x.r;x.l = t.l; x.r = t.r;if (v < p.key) p.l = x; else p.r = x;

Sletning af H

R

H

C

A

A I M P S

L

N

X

p

t

x

c

p.l eller p.r

K

Page 31: Søgning I

31

void remove(int v) { Node c, p, x, t; z.key = v; p = head; t = head.r; while (v != t.key)

{ p = t; t = v < t.key ? t.l : t.r; } if (t == z) return;

if (v < p.key) p.l = x; else p.r = x; }

if (t.r == z) x = t.l;

else if (t.r.l == z) { x = t.r; x.l = t.l; }

else { c = t.r; while (c.l.l != z)

c = c.l; x = c.l; c.l = x.r; x.l = t.l; x.r = t.r;}

Metoden remove

Page 32: Søgning I

32

Rekursiv udgave af remove

Node removeR(Node t, int v) {

if (t == z) return z;

if (v < t.key) { t.l = removeR(t.l, v); return t; }

if (v > t.key) { t.r = removeR(t.r, v); return t; }

if (t.r == z) return t.l;

t.r = removeMin(t.r);

min.l = t.l; min.r = t.r;

return min; }

Node min;

void remove(int v) { removeR(head, v); }

Page 33: Søgning I

33

Node removeMin(Node t) { if (t.l == z) { min = t; return t.r;

} t.l = removeMin(t.l); return t; }

RemoveMin(rekursiv udgave)

Page 34: Søgning I

34

void sort(Node n, Vector V) { if (n == z)

return; sort(n.l, V); V.addElement(n.info); sort(n.r, V);}

Sortering forbinære søgetræer

Inorder-gennemgang af det binære træ

Vector sort() { Vector V = new Vector(); sort(head.r, V); return V; }

Page 35: Søgning I

35

Balancerede søgetræer

Balancering er en teknik, der garanterer, at de værste tilfælde ved søgning ikke forekommer.

Ideen er at omorganisere træet under indsættelse, så det bliver fuldt (eller næsten fuldt).

I det følgende præsenteres et rød-sort-træ, en datastruktur, der garanterer O(logN) kompleksi-tet både for indsættelse og søgning.

Princippet i algoritmerne forklares dog bedst ved hjælp af datastrukturen et 2-3-4-træ.

Page 36: Søgning I

36

Et 2-3-4-træ er et søgetræ, hvor hver knude kan

have 2, 3 eller 4 udgående hægter (sønner).

2-3-4-træer

E H REn 4-knude:

≤ E (> E, ≤ H) (> H ≤ R) > R

H R

En 3-knude:

≤ H (> H, ≤ R) > R

REn 2-knude:

≤ R > R

Page 37: Søgning I

37

Eksempel på et 2-3-4-træ

E R

A C H I N S

Søgning i et 2-3-4-træ er simpel.

(1) Sammenlign søgenøglen med nøglerne i roden. (2) Find intervallet, der indeholder søgenøglen. (3) Følg den tilsvarende hægte (rekursivt).

Page 38: Søgning I

38

Indsættelse i et 2-3-4-træ

Foretag en mislykket søgning (søgning til bunden af træet).

Hvis 2-knude på bunden: konverter til 3-knude.

Hvis 3-knude på bunden: konverter til 4-knude.

Hvis 4-knude på bunden: ?

E R

H I N SA C

Page 39: Søgning I

39

Løsning: Sørg for at der aldrig opstår en 4-knude i bunden!

Transformere træet undervejs nedad. Enhver 4-knude, der mødes, “splittes” ved hjælp af én af følgende 3 transformationer:

D

E G H

D G

E H

Eksempel:

(1) Faderen er en 2-knude:

Page 40: Søgning I

40

(2) Faderen er en 3-knude:

D G

H K M

D G K

H M

Eksempel:

Page 41: Søgning I

41

D G K

Eksempel:

Invariant: Den aktuelle knude er ikke en 4-knude.Derfor er indsættelse i bunden let.

G

D K

(3, særtilfælde) Roden er en 4-knude:

Page 42: Søgning I

42

Eksempel på konstruktion af et 2-3-4-træ

A B C S

D I R

G H N

D

A B C SNE G H

I

R

Indsættelse af E (roden splittes)

Page 43: Søgning I

43

A B C SNE G H

D

I

R

Indsættelse af X (simpel)

A B C NE G H

D

I

R

S X

Indsættelse af F (transformation 1)

N

I

R

S X

D G

A B C E F H

Page 44: Søgning I

44

Kompleksitet

Søgning i 2-3-4 træer med N knuder besøger aldrig mere end log2N + 1 knuder.

Afstanden fra roden til ethvert blad er den samme. Kun i tilfælde 3 (roden er en 4-knude) øges træets højde. Afstanden til ethvert blad øges med 1.

Indsættelse i et 2-3-4 træ med N knuder kræver færre end log2N + 1 splitninger, og synes at kræve færre end 1 splitning i gennemsnit.

Page 45: Søgning I

45

void insert(int v) {Node p = head, x = head.r;while (x != z) { p = x; x = theRightLink(p, v); if (isFourNode(x)) x = split(x); }if (p == head) head.r = new TwoNode(v); else if (isTwoNode(p)) makeThree(p, v); else if (isThreeNode(p)) makeFour(p, v); }

Direkte implementation er kompliceret på grund af • håndtering af forskellige knudetyper • split skal kunne håndtere mange tilfælde

Skitse til implementation afindsættelse i 2-3-4-træer

Page 46: Søgning I

46

Ide: Repræsenter 2-3-4-træer som binære træer

med “indre” kanter for 3- og 4-knuder.

Rød-sort-træer

Gamle kanter kaldes sorte.

Nye kanter kaldes røde.

eller

Page 47: Søgning I

47

Invarianter (rød-sort-egenskaberne): (1) Der er aldrig to konsekutive røde kanter på en vej. (2) Enhver vej fra roden til en ekstern knude indeholder det samme antal sorte kanter.

Transformation af 2-3-4-træ til rød-sort-træ

C F I

K

N R

A D E G H J L M P S X

K

F

C

A

D

E G

I

J

R

N S

M

LH

P X

Page 48: Søgning I

48

Søgning og indsættelsei rød-sort-træer

Sletning er også simplere (men behandles ikke her).

Indsættelse er simplere end for 2-3-4 træer, fordi antallet af splitningstilfælde reduceres.

Søgning er sædvanlig søgning i binære søgetræer.

En søgning i et rød-sort-træ med N knuder kræver færre end 2log2N + 2 sammenligninger.

En søgning i et rød-sort-træ med N knuder, der er bygget ud fra tilfældige nøgler bruger i gennemsnit cirka 1.002log2N sammenligninger.

Page 49: Søgning I

49

Splitningstilfælde

(1)

Skift farve på 3 kanter

Page 50: Søgning I

50

Skift farve på 3 kanter

(2)

Page 51: Søgning I

51

Vanskelige tilfælde

(3)

?

Page 52: Søgning I

52

(4)

?

Page 53: Søgning I

53

En rotation omordner et træ ved at ændre 2 hægter.

Ordenen for et binært søgetræ bevares.

Højre-rotation:

Rotationer

B

A

A

B

A

B

B

A

Venstre-rotation:

Page 54: Søgning I

54

Node rotateR(Node h) {

Node x = h.l; h.l = x.r; x.r = h;

return x;

}

Rotationer i Java

Node rotateL(Node h) { Node x = h.r; h.r = x.l; x.l = h; return x; }

h

x

x

h

Page 55: Søgning I

55

Rotationer kan retablere rød-sort-egenskaben

HøjrerotationB

A

A

B

A B

A

Venstrerotation

B

Page 56: Søgning I

56

Dobbeltrotation

C

B

A

VenstrerotationB

CA

Højrerotation

B

C

A

Page 57: Søgning I

57

Hver knude forsynes med en boolean, red, som er true,

hvis og kun hvis kanten op til faderen er rød.

class Node {

int key; String info;

Node l, r;

boolean red;

Node(int k, String i,

Node ll, Node rr,

boolean rd) {

key = k; info = i;

l = ll; r = rr;

red = rd;

}

}

class Node

Page 58: Søgning I

58

Rekursiv indsættelse i rød-sort-træer

Node insertRB(Node t, int v, String info, boolean right) {

if (t == z) return new Node(v,info,z,z,true);

if (t.l.red && t.r.red) { t.red = true; t.l.red = t.r.red = false;

}

if (v < t.key) { t.l = insertRB(t.l,v,info,false);

if (t.red && t.l.red && right) t = rotateR(t);

if (t.l.red && t.l.l.red) { t = rotateR(t); t.red = false; t.r.red = true;

} }

else { ... }

return t; }

Page 59: Søgning I

59

De to specielle tilfælde

if (t.red && t.l.red && right)

t = rotateR(t);

t t

if (t.l.red && t.l.l.red) { t = rotateR(t); t.red = false; t.r.red = true;}

t t

Page 60: Søgning I

60

else { t.r = insertRB(t.r,v,info,true); if (t.red && t.r.red && !right)

t = rotateL(t); if (t.r.red && t.r.r.red) { t = rotateL(t); t.red = false; t.l.red = true; }}

Tilfældet v > t.key er analogtmed tilfældet v < t.key

l er erstattet med r, r er erstattet med l, right er erstattet med !right, og rotateR er erstattet med rotateL

Page 61: Søgning I

61

void insert(int v, String info) {

head = insertRB(head, v, info, false);

head.red = false;

}

Metoden insert

Page 62: Søgning I

62

Konstruktøren i Dictionary

Dictionary() { z = new Node(0, null, null, null, false); z.l = z.r = z; head = z; }

Node z, head;

Page 63: Søgning I

63

• AVL-træ (Adel´son-Vel’skii og Landis) (højdebalanceret træ)

• Vægt-balanceret træ

• Splay-træ

• B-træ (meget velegnet til ekstern søgning)

• 2-3-træ

• AA-træ

• Randomiseret træ

• Skipliste - et alternativ til binære træer

Rød-sort-træ og skipliste anbefales

Andre datastrukturer til realisering af binære søgetræer

Page 64: Søgning I

64

Ugeseddel 621. oktober - 27. oktober

• Læs kapitel 16 i lærebogen (side 231-244)

• Forbered diagnostisk prøve i al gennemgået stof

• Løs følgende opgaver

1. Opgave 14.5, 14.7, 14.9 og 14.10.

2. Opgave 15.1 og 15.2.