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