Föreläsning 12 Innehåll Binärt sökträd Binära sökträd AVL

Innehåll
• Sökträd
• Sökning
Föreläsning 12
Sökning och Sökträd
365
366
Binärt sökträd
Binära sökträd
• Används för sökning i linjära samlingar av
dataobjekt, specifikt för att konstruera tabeller
och lexikon.
• Organisation:
– Ett binärt träd som är sorterat med avseende på en
sorteringsordning R av etikett-typen så att
• I varje nod N gäller att alla etiketter i vänster delträd går
före N som i sin tur går före alla etiketter i höger delträd.
• Alla noder är definierade.
• Om trädet är komplett så vet vi att både
medel- och värstafallskomplexiteten är
O(log n).
• Men… Det tar tid och kraft att se till att
trädet är komplett. Ibland kan man tvingas
bygga om hela trädet.
• Det räcker att se till att balansen är god…
367
368
AVL-träd
Exempel på ett AVL-träd
• Kallas även höjdbalanserat binärt sökträd.
– Adelson – Velskii and Landis
• Noderna är fördelade så att trädet är väl balanserat. För varje
enskild nod gäller:
– Höjden för vänster och höger delträd skiljer sig åt med högst 1.
• Vi får värstafallskomplexitet för sökning O(log n) utan att
försämra komplexiteten för insättning och borttagning (som alltså
också är O(log n)).
– Algoritmerna för insättning och borttagning blir lite bökigare att konstruera.
– Måste lagra information om höjden på
delträden i noderna.
369
44 3
17 1
78 2
0
50 1
32
48
0
88
62
0
0
370
1
Insättning i ett AVL-träd
• Det nya elementet gör att trädhöjden förändras
och att trädet måste höjdbalanseras.
– Man kan hålla reda på delträdens höjd på olika sätt:
• Lagra höjden explicit i varje nod
• Lagra en balansfaktor för noden (-1, 0, +1 men kan
temporärt bli +/-2)
– Balansen = h(vänster barn) – h(höger barn)
• Förändringen brukar beskrivas som en höger- eller
vänsterrotation av ett delträd.
• Det räcker med en rotation för att få trädet i balans
igen.
371
Exempel: Insättning i ett AVL-träd
Insättningsalgoritm
• Starta från den nya noden och leta uppåt tills man
hittar en nod x så att ”grandparent” z är obalanserat.
Markera x:s förälder y.
• Gör en rekonstruering av trädet så här:
– Döp om x, y, z till a, b och c baserat på inorder-ordning.
– Låt T0, T1, T2 och T3 vara delträden till x, y och z i
inorder-ordning. (Inget av delträden får ha x, y eller z
som rot.)
– z byts ut mot b, dess barn är nu a och c.
– T0 och T1 är barn till a och T2 och T3
är barn till c.
372
Exempel: Insättning i ett AVL-träd
b
44 4
z/c
17 1
a
44 3
T0
c
T1
T2
T3
78 3
17 1
y/a
0
50 2
32
T0
48
0
T3
x/b
62
88
62 2
0
32
0
50 1
78 1
1
48
0
54
0
88
0
T1 54
373
Fyra olika rotationer (1)
374
Fyra olika rotationer (2)
T0
Om b = y kallas det en enkel rotation
”Rotera upp y över z”
T1
T2
T3
Om b = y kallas det en enkel rotation
”Rotera upp y över z”
375
376
2
Fyra olika rotationer (3)
Fyra olika rotationer (4)
T0
T1
T3
T1
T0
T
T2
Om b = x kallas det en dubbel rotation
”Rotera upp x över y och sedan över z”
Om b = x kallas det en dubbel rotation
”Rotera upp x över y och sedan över z”
377
Ett annat sätt att beskriva det på
Anta att vi har
balans…
378
Ett annat sätt att beskriva det på
y
Anta att vi har
balans…
x
y
x
T2
T0
T2
T1
T0
379
Ett annat sätt att beskriva det på
y
T1
. . . och sen stoppar in
något som sabbar den
380
Ett annat sätt att beskriva det på
Gör en enkel rotation.
y
x
Gör en enkel rotation.
x
T2
T0
T3
2
T2
T1
T1
T0
381
382
3
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
Gör en enkel rotation.
y
x
Gör en enkel rotation.
x
T2
T1
T1
T0
T2
T0
383
Ett annat sätt att beskriva det på
384
Ett annat sätt att beskriva det på
Gör en enkel rotation.
x
x
y
T1
y
T0
T2
T0
T1
T2
385
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
Gör en enkel rotation.
x
386
y
I ett steg:
Klart!
y
x
T2
T0
T1
T1
T2
T0
387
388
4
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
Ett nytt exempel…
x
y
T0
z
y
T2
T1
389
390
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
z
z
y
Prova en enkel rotation
igen…
y
. . . den här gången stoppar
vi in något här
391
Ett annat sätt att beskriva det på
392
Ett annat sätt att beskriva det på
z
y
z
… hmm vi har inte
fått balans…
393
Börja om från början…
y
394
5
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
z
z
y
Vi får lov att göra
en dubbel rotation
y
T0
… och titta på
strukturen i Y
T3
x
T2
T1
395
396
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
z
z
y
y
T3
x
T0
T0
T2
T1
T3
x
T2
T1
397
398
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
z
z
y
y
T3
x
T0
T1
T0
T2
T1
399
T3
x
T2
400
6
Ett annat sätt att beskriva det på
Ett annat sätt att beskriva det på
x
x
z
y
T0
T1
T2
z
y
T0
T3
T1
T2
Klart!
T3
401
Ett annat sätt att beskriva det på
z
402
Ett annat sätt att beskriva det på
Double rotation
in one step . . .
x
y
T3
x
T0
T1
z
y
T0
T1
T2
T3
T2
403
404
Flervägs sökträd
Borttagning ur ett AVL-träd
• Borttagningen börjar som en vanlig borttagning ur ett binärt
sökträd.
• Men även borttagning ur ett AVL-träd kan störa balansen.
– Vi gör en rotation som tidigare för att återställa den (behövs bara enkla
rotationer).
• När vi återställer balansen på ett ställe kan det uppstå obalans på
ett annat…
– Måste upprepa balanseringen (eller kontroll av balansen) till dess vi nått
roten.
405
• Ett m-vägs sökträd (m-way search tree, m-ary
search tree) är en generalisering av ett binärt
sökträd.
• Trädet är ett ordnat träd där varje nod har högst m
delträd.
• Etiketterna är sekvenser av upp till m-1 värden i
stigande sorteringsordning som fungerar som
delningspunkter vid sökning.
• Oftast är etiketterna nycklar och värdet till en viss
nyckel finns i lövet.
406
7
Flervägs sökträd
B-träd
• Till en nod med k+1 delträd, t0, t1, …, tk hör en
sekvens med värden v1, v2, …, vk.
Sorteringsvillkoret för trädet är att:
– alla värden i t0 går före v1 (i sorteringsordningen)
– alla värden i tj ligger mellan vj och vj+1 för 1<j<k
– alla värden i tk går efter vk
• Operationerna blir liknande de för binärt sökträd.
• Plattare träd. Höjden = logm n
• Mer jobb i noderna
• Ett B-träd av ordning m är en typ av
balanserat m-vägs sökträd som uppfyller
följande:
– Roten är antingen ett löv eller har minst två
barn
– Alla noder utom roten och löven har mellan
m/2 och m barn
– Alla löv är på samma djup
407
408
B-träd
B-träd analys:
• Insättning av nya element görs alltid på den
djupaste nivån, i rätt löv för att bevara
sorteringsordningen.
– En insättning kan leda till att noden blir för stor (dvs
> m). Då måste noden delas upp.
• Borttagning kan leda till att man måste justera
värdena och slå ihop noder eller omfördela
värden mellan dem.
• B-träd av ordning 3 kallas också
2-3 träd
• För ett B-träd av ordning m, med höjden h och n nycklar
insatta gäller h = O(log n).
• För att välja rätt underträd vid sökning krävs att man
stänger in sökt nyckel mellan två nycklar i noden. Om
nycklarna är sorterade och lagrade i en vektor kan man
använda binärsökning. Sökning i en nod O(log m)
• Nycklarna i vektorn måste skiftas runt vid splittring av en
nod. Kostnad O(m)
• Eftersom m är en konstant blir det O(1) arbete i varje nod
vid sökning och insättning. Antalet noder som berörs är
uppåt begränsad av höjden.
• Värstafallskostnad för sökning och
insättning O(log n)
409
Exempel på B-träd: 2-4 träd
410
Exempel på B-träd: 2-4 träd
• Regel för borttagning:
• Varje nod har 1, 2 eller 3 nycklar och varje
icke-löv har 2-4 barn.
• Regel för insättning:
– Man letar sig fram till rätt löv på liknande sätt som i
ett vanligt sökträd.
– Den nya nyckeln sätts in där. Om det blir för många
nycklar i det lövet splittras det.
411
– Man letar sig fram till rätt löv på liknande sätt som i
ett vanligt sökträd.
– Enkla fallet: Det finns flera nycklar i noden, ta bort
den som ska bort.
– Halvsvåra fallet: Syskonen har ”extra” element som
vi kan sno.
– Svåra fallet: Vi får tomt och syskonet har bara ett
element. Då måste vi göra en ”fuse”operation.
412
8
Binär sökning
Linjär sökning
• Starta från början och sök tills elementet hittat
eller sekvensen slut.
• Komplexitet
– Elementet finns: I medel gå igenom halva listan, O(n)
– Elementet saknas: I medel gå igenom hela listan, O(n)
• Om listan är sorterad:
– Elementet saknas: Räcker i medel att leta genom
halva listan n/2, O(n)
• Om sekvensen har index (tex i en array eller numrerad lista) kan
man söka binärt.
• Successiv halvering av sökintervallet.
• Vi får värsta-falls och medelkomplexitet O(log n).
• Jämför med elementet närmast mitten i intervallet.
– Om likhet – klart!
– Om det sökta värdet kommer före i sorteringsordningen fortsätt sökningen
rekursivt i det vänstra delintervallet.
– Om det kommer efter i sorteringsordningen fortsätt sökningen rekursivt i det
högra delintervallet.
413
414
Exempel:
Strängsökning
• 1 2 4 4 6 7 9 13 14 19
• Sök efter elementet 13.
• Specialfall av sökning.
– Man söker inte ett enstaka element utan en
sekvens av element.
– Elementet ofta tecken, men kan även vara andra
typer av data.
– Linjär sökning: 8 jämförelser innan träff.
– Binär sökning: 2 jämförelser innan träff.
• Sök efter elementet 10
– Linjär sökning: 8 jämförelser innan man ger upp.
– Binär sökning: 4 jämförelser innan man ger upp.
• Formellt:
– Vi har ett mönster P med längd m och vi söker i
en sekvens S av längd n där m<<n.
415
Strängsökning…
Första försök till algoritm:
• Börja jämföra mönstret med sekvensen med start i
position ett.
• Ett antal algoritmer
–
–
–
–
416
Naiv
Knuth Morris Pratt
Booyer Moore
Rabin-Karp
– Jämför mönstret från vänster till höger tills man
misslyckas.
– Flytta då fram en position i sekvensen och försök igen.
• Värsta fallet: Varje element i S avläses m gånger,
dvs O(n*m)
– I praktiken bättre
417
418
9
Exempel
Knuth Morris Pratt
• Utnyttjar en felfunktion f
– som berättar hur mycket av mycket av den senaste
jämförelsen man kan återanvända om man felar
– är definierat som det längsta prefixet i P[0,...,j] som
också är suffix av P[1,...,j] där P är vårt mönster.
– visar hur mycket av början av strängen matchar upp till
omedelbart före felet
419
420
KMP-algoritmen
Felfunktion exempel
•
•
Input: String T (text) with n characters and P (pattern) with m characters.
Output: Starting index of the first substring of T matching P, or an indication that P is not a
substring of T.
f <- KMPfailureFunction(P)
i <- 0
j <- 0
while i < n do
if P[j] = S[i] then
if j = m-1 then return i-m-1 // En matchning
i <- i+1
j <- j+1
else if j > 0 then
// ingen match, vi har gått j index direkt efter
// matchande prefix i P
j <- f(j-1)
else
i <- i+1
return ingen matchning av delsträngen P i S
• Om jämförelsen felar på position 4, så vet vi
att a,b i position 2,3 är identiska med position
0,1
j
0
P[j] A
1
B
2
A
3
B
4
A
5
C
f(j) 0
0
1
2
3
0
421
422
KMPfailureFunction(P)
KMP exempel
i
i <- 1
j <- 0
while i < m-1 do
if P[j] = P[i] then
f(i)<- j+1
i <- i+1
j <- j+1
else if j > 0 ingen match then
j <- f(j-1) //j index efter pref som match. prefix
else //ingen matchning
f(i) <- 0
i <- i+1
423
a b a c a a b a c c a b a c a b a a a a a a
a b a c a b
0 1 2 3 4 5
j
a b a c a b
a b a c a b
a b a c a b
a b a c a b
j
0 1 2 3 4 5
P[j]
a b a c a
f(j)
0 0 1 0 1 2
b
424
10
Boyer-Moores algoritm
KMP-Algoritmen
• Två idéer:
– Gör matchningen baklänges, med start i mönstrets
sista element.
– Utnyttja kunskap om mönstrets uppbyggnad och
informationen om värdet på den första felmatchande
elementet i S för att flytta fram mönstret så långt som
möjligt varje gång.
• Låt k = i - j
• För varje varv i while-loopen händer ett av följande:
– S[i] = P[j], öka i och j med 1, k oförändrad.
– S[i] < > P[j] och j > 0, i är oförändrad men k ökar med minst 1
eftersom den ändras från i - j till i - f(j-1)
– S[i] < > P[j] och j = 0, öka i med 1 och minska k med 1 (ty j
oförändrad).
• Alltså för varje varv i loppen ökar antingen i eller k med
minst 1. Max antal varv blir 2n
• Detta antar att f redan är beräknad (som är O(m)).
• Total komplexitet: O(n + m)
• Om det finns upprepningar av element i mönstret så får man
bara flytta fram till den högraste förekomsten.
• Förskjutningstabell talar om hur långt man får flytta.
425
Rabin-Karp
426
Rabin-Karp exempel
• Beräkna ett hashvärde för mönstret och för varje
delsträng av texten som man ska jämföra med
• Om hashvärdena är skilda, beräkna hashvärdet för
det nästa M tecknen i texten
• Om hashvärdena är lika, utför en brute-force
jämförelse mellan P och delsträngen
• Med andra ord:
– Endast en jämförelse per deltext
– Brute-force endast när hashvärdena matchar.
Hashvärdet för AAAAA = 37
Hashvärdet för AAAAH = 100
AAAAAAAAAAAAAAAA
AAAAH
100 <> 37
AAAAAAAAAAAAAAAA
AAAAH
100 <> 37
AAAAAAAAAAAAAAHA
AAAAH
100 = 100
427
Rabin-Karp hashfunktionen
428
Rabin-Karp hashfunktionen
• Vilken? Den får inte kosta för mycket....
• Betrakta de nästa M tecknen i söksträngen som ett
M-siffrigt tal i basen b, där b är antalet bokstäver i
alfabetet
• Textsekvensen t[i..i+M-1] avbildas på talet
x(i) = t[i]b M-1+ t[i+1]b M-2+...+ t[i+M-1]
429
• Billigt att beräkna x(i+1) från x(i)
x(i+1) = t[i+1]bM-1+ t[i+2]b M-2+...+ t[i+M]
• x(i+1) = x(i)b skifta ett vänster,
- t[i]bM
ta bort den vänstraste termen
+ t[i+M]
lägg till den nya högertermen
• Behöver inte räkna om hela talet utan gör bara en
justering för det nya tecknet
430
11
Hash-värdet fortsättning
Algoritm
• Om M är stort blir blir (bM) enormt därför så
hashar man med mod ett stort primtal q
• h(i) = ((t[i]bM-1mod q) + (t[i+1]bM-2mod q)+...+
(t[i+M-1] mod q)) mod q
• h(i+1) = (h(i)b mod q
- (t[i]b Mmod q)
+ (t[i+M] mod q) mod q
hash_M <- Beräkna hashvärdet för M
hash_S <- Beräkna hashvärdet för
den första delsträngen
do
if (hash_M = hash_S) then
Bruteforce jämförelse av M och S
hash_S + 1 tecken beräknas
while end of text or match
431
432
Komplexitet
• Om det är tillräckligt stort primtal q för
hashfunktionen så kommer hashvärdet från
två mönster vara distinkta
• I detta fall så tar sökningen O(N) där N är
antalet tecken i strängen
• Men det finns alltid fall som ger i närheten
av värsta fallet O(N*M) om primtalet är för
litet
433
12