Föreläsning 10 Innehåll Flödet i en graf Flödet i en graf

Innehåll
•
•
•
•
Föreläsning 10
Mängd, lexikon och
hashtabell
Flödet i en graf
Mängd
Lexikon
Hashtabell
294
Flödet i en graf
• Riktad graf med vikter cv,w, som anger flödeskapacitet
över bågen (v,w).
• Kapaciteten kan t.ex. vara mängden vätska som kan
flöda genom ett rör, maximala mängden trafik på en väg
eller kommunikationskapaciteten i ett datornät.
• Grafen har två noder s (source) och t (sink) och
uppgiften är att beräkna det maximala flödet mellan s
och t.
• Genom varje båge (u,v) kan vi maximalt ha ett flöde på
c(u,v) enheter.
• För varje node v gäller att det totala inkommande flödet
måste vara lika med det utgående flödet.
296
295
Flödet i en graf
• Ett flödesnätverk består av
– En riktad graf
– Vikter c(u,v) som kallas kapaciteter på bågarna
• Ickenegativa vikter
– Två speciella noder
• Källan (source), betecknas ”s”, en nod utan
ingående bågar
• Avloppet (sink) betecknas ”t”, en nod utan utgående
bågar
297
1
Kapacitet och flöde
Maximumflödes problem
• Flödet är en funktion på kanterna:
– 0 ≤ flöde ≤ c(u, v)
– Flödet in till noden = flödet ut ur noden
– Värde/value: Det kombinerade flödet in till avloppet.
s
2
3
2
1
1
2
2
2
1
11
2
4
2
0
2
3
1
22
t
1
2
2
2
1
2
4
1 2
1
2
1
3
2
1
02
4
1 1
2
1 1
2
2
2
1
t
11
0
2
2
s
s
2
2 2
0
1
2
t
• Givet ett nätverk N,
hitta ett flöde med
maximalt värde
←Ett exempel på
maximalt flöde
Värde = 5
1
1
298
Förbättrande (augmenting) flöde
s
s
2
1
2
2
2
1
1
1
2
2
2
2
Nu har flödesvärdet
ökat till 4!!
1
2
2
– flödet(u,v) > 0
– Flödet kan minskas!
2
0
u
• Bakåtriktade bågar
Förbättrande väg
(Augmenting path)
s
2
– flödet(u, v) < c(u, v)
– Flödet kan ökas!
2
t
t
v
• Framåtriktade bågar
0
2 2
Ökande väg
2
1
Nätverk med flödesvärde 3
299
v
u
2 2
t
300
301
2
Maximala flödesteoremet + algoritm
• Ett flöde har maximum värde om och endast om
nätverket inte har någon förbättrande väg.
• Ford & Fulkerson algoritmen:
Initialisera nätverket med noll flöde
Anropa metoden findFlow() som definieras
Om det finns förbättrande vägar:
Hitta en av dem
Öka flödet
Anropa (rekursivt) findFlow()
Initiera nätverket med nollflöde.
Kapaciteterna i svart ovan bågarna
och flödet i grönt nedan bågarna.
Skicka igenom ett enhetsflöde genom
nätverket. Flödesvägen markerat med
rött och de förbättrade flödesvärdena i
blått.
302
Skicka ytterligare ett enhetsflöde
genom nätverket.
Skicka igenom ytterligare ett enhetsflöde genom nätverket. Notera att det
finns ytterligare en förbättrande väg
som går genom kanten i mitten.
304
303
Skicka ett enhetsflöde genom den
förbättrande vägen. Nu finns det inga
fler förbättrande vägar. Alltså…
Vi har hittat detta nätverks maximala
flöde!
305
3
s
2
Hur gör man mer specifikt?
1
0
2
0
t
s
2
0
(-, ∞)
0
0
a (s, 2)
s märks som stängd och (-, ∞) (dvs inte öppnats
från någon nod flödet kan förändras oändligt)
b
2
0
Första vägen från s till t:
2
0
0
a
• Hur vet man att det finns en förbättrande
väg?
• Hur vet man vilken av de förbättrade
vägarna man ska ta först?
• Läs mer i boken.
(-, ∞)
2
1
b
0
Bågarna från s traverseras, noderna i andra
änden markeras öppna och märks med deras
maximala kapacitet och sätts in i prio-kön.
(s, 2) q = (a, b)
2
2
0
t
306
s
2
0
0
0
a (s, 2)
(-, ∞)
2
1
b
0
2
2
t
0
Första noden tas från kön (a) och markeras
stängd. Dess bågar undersöks i tur och
ordning.
(s, 2)
Bågen (a,s) leder till stängd nod och (a, b)
leder till öppen nod - inget händer. Bågen
(a,t) leder till en ny nod t som markeras (a, 2)
307
s
2
b
0
2
Nu har vi nått fram till t, traverseringen avbryts.
Man följer stegen bakåt till s och markerar
bågarna samtidigt som noderna avmarkeras.
2
t
2
2
0
s
2
a
0
2
(b, 0)
(s, 2)
1
b
2
2
t
308
2
0
2
s märks som stängd och (-, ∞)
Bågarna från s traverseras. Eftersom vägen till a inte
kan ökas mer struntar vi i den. q=(b)
(s, 2)
Första noden tas från kön (b) och markeras stängd. Dess
bågar undersöks i tur och ordning. Bågen (b,s) leder
till stängd nod - inget händer.
Bågen (b,a) leder till en ny nod a (baklänges) som kan
markeras med maximalt det nuvarande flödet, dvs (b,
0).
Bågen (b,t) leder till en ny nod t som markeras (b, 2).
q=(a,t)
0
t
(-, ∞)
2
1
2
b
0
2
Andra vägen från s till t:
2
1
a
(a, 2)
0
2
a
0
2
s
2
(-, ∞)
0
(b, 2)
309
4
(-, ∞)
Första noden tas från kön (a) och markeras stängd. Dess
bågar undersöks i tur och ordning. Bågen (a,s) och (a,
b) leder till stängda noder och (a, t) till en öppen nod (s, 2) inget händer.
s
2
a
0
2
(b, 0)
2
1
0
2
2
2
t
0
Första noden tas från kön (t). Vi har nått t. Traversering
avbryts och vi går bakåt och avmakerar allt.
(b, 2)
Om vi nu försöker börja om igen så hittar vi inget nytt
eftersom både (s,a) och (s,b) utnyttjas maximalt.
s
2
2
2
2
1
a
b
0
2
Mängd
b
• Modell: En påse men den är inte riktigt bra eftersom man
tex kan ha mängder med gemensamma element.
• Organisation:
– En oordnad samling av element som är av samma typ.
– Grundmängden behöver inte vara ändlig (heltal) men dataobjekten
är ändliga.
– Kan inte innehålla två likadana värden.
– En mängd kan inte innehålla mängder.
2
2
t
2
310
Specifikation
311
Konstruktion av mängd
• Alla metoder som finns i boken behövs inte.
– Empty, IsEmpty, Insert, Choose och Remove skulle
räcka.
– Man har tagit med de vanliga matematiska
mängdoperationerna.
• Vill man kunna ha mängder av mängder måste
man skapa en ny datatyp generaliserad mängd på
samma sätt som generaliserad lista.
312
• Nästan oavsett konstruktion måste man
kunna hantera det faktum att det inte får
finnas dubbletter i en mängd.
• Mängd som lista har två alternativ:
– Se till att listan inte har dubbletter (krav på SetInsert och Union)
– Låt listan ha dubbletter (krav på Equal,
Remove, Intersection, Difference)
313
5
Konstruktion av mängd som lista
• Komplexitet:
Konstruktion av mängd som bitvektor
• En bitvektor är en vektor med elementvärden av
typen Bit = {0,1}
– Metoder som kräver sökningar i listan O(n)
– Binära mängdoperationerna kräver (pga nästlad iteration)
O(m*n)
– Listan kan konstrueras på olika sätt. Sorterad lista tex är
effektivare för de binära mängdoperationerna.
– Ibland tolkas 0 och 1 som falskt resp. sant, dvs Bit
identifieras som datatypen Boolean
+ Grundmängden kan vara mycket stor
+ Delmängderna kan både vara mycket små och mycket
stora utan utrymmesförluster (om man implementerar
listan på rätt sätt)
- Flera operationer blir slöa jämfört
med andra val.
• Grundmängden måste ha en diskret linjär
ordning av elementen. (Man kan numrera dem.)
• Bit k i bitvektorn motsvarar det k:te elementet i
grundmängden. Biten är 1 om
elementet ingår i mängden.
314
Konstruktion av mängd som bitvektor
• Om bitvektorn finns implementerad som ett eller flera
ord i datorns primärminne kan man utnyttja
maskinoperationer.
– Dvs man gör operationen samtidigt på alla element i vektorn.
Detta gör många metoder effektiva.
+ Relativt effektiv i allmänhet, mycket effektiv som ovan.
- Grundmängden måste vara ändlig och i praktiken rätt så
liten. Reserverar minne proportionellt mot
grundmängdens storlek.
- Om man har många små mängder i en
tillämpning blir det dålig effektivitet i
minnesutnyttjandet.
316
315
Mängd konstruerad
som boolesk funktion
• En mängd kan representeras av sin
karakteristiska funktion χs(x):
χs(x) = 1 om x ∈S
0 annars
317
6
#include <stdio.h>
#include <stdlib.h>
#define MAXLENGTH 100
typedef struct intSet_s{
int s[MAXLENGTH];
}intSet_t;
Implementation
av heltalsmängd
som boolesk
vektor (1)
/* "Interface" */
intSet_t *intSet_empty();
intSet_t *intSet_single(int num);
intSet_t *intSet_insert(int num, intSet_t *set);
intSet_t *intSet_union(intSet_t *t, intSet_t *set);
intSet_t *intSet_intersection(intSet_t *t, intSet_t *set);
intSet_t *intSet_difference(intSet_t *t, intSet_t *set)
int intSet_isEmpty(intSet_t *set);
int intSet_memberOf(int num, intSet_t *set);
int intSet_choose(intSet_t *set);
intSet_t *intSet_remove(int num, intSet_t *set);
int intSet_equal(intSet_t *t, intSet_t *set);
int intSet_subSet(intSet_t *t, intSet_t *set);
void intSet_printOut(intSet_t *set);
318
intSet_t *intSet_intersection(intSet_t *t, intSet_t *set){
intSet_t *newSet = intSet_empty();
int i;
intSet_t *intSet_empty(){
intSet_t *set = malloc(sizeof(intSet_t));
return set;
}
intSet_t *intSet_single(int num){
intSet_t *set = malloc(sizeof(intSet_t));
set->s[num] = 1;
return set;
}
intSet_t *intSet_insert(int num, intSet_t *set){
set->s[num] = 1;
return set;
}
Implementation
(2)
intSet_t *intSet_union(intSet_t *t, intSet_t *set){
intSet_t *newSet = intSet_empty();
int i;
for(i=0;i<MAXLENGTH;i++){
if (set->s[i] || t->s[i])
newSet = intSet_insert(i, newSet);
}
return newSet;
}
int intSet_isEmpty(intSet_t *set){
int i;
for(i=0;i<MAXLENGTH;i++){
if (set->s[i])
return 0;
}
return 1;
Implementation
(3)
for(i=0;i<MAXLENGTH;i++){
if (set->s[i] && t->s[i])
newSet = intSet_insert(i, newSet);
}
return newSet;
319
Implementation
(4)
}
}
int intSet_memberOf(int num, intSet_t *set){
return set->s[num];
}
intSet_t *intSet_difference(intSet_t *t, intSet_t *set){
intSet_t *newSet = intSet_empty();
int i;
int intSet_choose(intSet_t *set){
int i;
for(i=0;i<MAXLENGTH;i++){
if (set->s[i] && !t->s[i])
newSet = intSet_insert(i, newSet);
}
return newSet;
for(i=0;i<MAXLENGTH;i++){
if (set->s[i])
return i;
}
return -1;
}
320
}
321
7
intSet_t *intSet_remove(int num, intSet_t *set){
set->s[num] = 0;
return set;
}
”Doping”
• For- eller while- loopar med break/return
int intSet_equal(intSet_t *t, intSet_t *set){
int i;
– Svåröverskådlig kod
– Inte entydigt när metoden avslutas
int isEmpty(intSet_t *set)
{
int i;
for(i=0;i<MAXLENGTH;i++)
{
if (set->s[i])
return 0;
}
return 1;
}
int isEmpty(intSet_t *set)
{
int i=0;
int tmp = 1;
while(tmp==1 && i < MAXLENGTH)
{
if (set->s[i])
tmp = 0;
i = i+1;
}
return tmp;
}
322
for(i=0;i<MAXLENGTH;i++){
if (set->s[i] != t->s[i])
return 0;
}
return 1;
Implementation
(5)
}
int intSet_subSet(intSet_t *t, intSet_t *set){
int i;
for(i=0;i<MAXLENGTH;i++){
if (t->s[i] && !set->s[i])
return 0;
}
return 1;
}
323
void intSet_printOut(intSet_t *set){
int i;
printf("{");
for (i=0; i<MAXLENGTH; i++){
if(set->s[i]==1)
printf("%d, ", i);
}
printf("}");
}
Implementation
(6)
int main(int argc, char *argv[]){
intSet_t *testSet = intSet_empty();
Lexikon
• En förenklad mängd där man tagit bort union, intersection,
difference, subset och equal.
• Kvar är det man behöver för att kunna hålla ordning på en
samling objekt:
–
–
–
–
–
printf("Isempty? %d\n", intSet_isEmpty(testSet));
testSet = intSet_insert(4, testSet);
testSet = intSet_insert(7, testSet);
testSet = intSet_insert(2, testSet);
printf("Isempty? %d\n", intSet_isEmpty(testSet));
intSet_printOut(testSet);
return 0;
empty
isempty(s)
insert(v, s)
remove(v, s)
memberOf(v, s)
}
324
325
8
Lexikon
Konstruktion av lexikon
• Ett lexikon är som en tabell utan tabellvärden.
• Saknar alltså metoder för att kombinera
lexikon till nya lexikon.
• Lexikon är en solitär datatyp. Man kan bara
göra modifieringar av isolerade dataobjekt.
• En icke-solitär datatyp har stöd för att
kombinera/bearbeta två eller flera dataobjekt.
• Vi kan få betydligt effektivare konstruktioner
när antalet och typen av metoder är begränsade.
• Vi kommer titta på två konstruktioner av
lexikon:
– Lexikon som Hashtabell
– Lexikon som Binärt sökträd (nästa föreläsning)
• Eftersom Lexikon är så likt Tabell kan dessa
konstruktioner med små ändringar bli effektiva
konstruktioner av en tabell.
326
327
Hashfunktion
Hashtabell
• Problemen med att implementera en mängd/lexikon
som en bitvektor är att grundmängden inte får bli för
stor och att många små mängder slösar med minnet.
– Vore bra att kunna komprimera bitvektorerna.
• Vi behöver en hashfunktion som avbildar den större
indextypen A till en mindre indextyp B.
• Man kan också se det som att grundmängden är
nycklarna i en tabell.
– Eller att man vill omvandla nycklar i en tabell till siffror
med hjälp av hashfunktionen.
328
• Funktionen f(x) avbildar en stor mängd nycklar, A, på en liten
mängd tal, B.
• Kollisioner är oundvikliga och en bra hashfunktion
kännetecknas av:
– Litet förväntat antal kollisioner
– Sprider elementen jämt över mängden.
– Bör påverkas av alla delar av nyckeln
• Kollisioner kan hanteras med
– Sluten hashing
– Öppen hashing
329
9
Sluten hashing
Exempel: Sluten hashing, linjär
teknik
• Låt B vara en cirkulär vektor, dvs efter det sista elementet
följer det första.
• Linjär teknik för kollisioner
• Grundmängden 1, 2, …, 125 ska avbildas på värdena
0, 1, ..., 6.
• Mängd = {1, 8, 27, 64, 125}
Använd hashfunktionen f(x) = x % 7.
– Om ett element kolliderar (platsen redan upptagen) sätter man in
det på den första lediga platsen efter den upptagna.
• Ger upphov till klustring i tabellen.
– Sökning efter ett element börjar på den plats dess hashvärde
anger och fortsätter eventuellt framåt. Om det inte påträffats före
nästa lediga plats så finns det inte i tabellen.
– Om vi vid borttagning i tabellen bara lämnar platsen tom blir det
fel vid sökning.
• Sätt in en ”borttagen” markör i tabellen.
0
1
2
3
4
0
1
h(1)
=
1
0
1
1
2
8
3
4
h(8)
=
1
0
0
125
1
1
h(64) 2
= 3
64
1
4
64
1
1
1
1
2
8
h(27) 2
= 3
6
4
8
3
4
5
5
5
6
6
6
5
27
6
8
h(125)
=
6
5
27
6
27
330
331
Hashtabeller - fyllnadsgrad
Sluten hashing, linjär teknik
• Värstafallskomplexiteten för samtliga
operationer är O(n) där n är antalet element
som finns insatta i tabellen.
– Är dock ytterst osannolikt. Alla element måste
ligga i en följd.
• Under förutsättning att tabellen inte fylls
mer än till en viss del får man i medeltal
O(1) för operationerna.
332
• En hashtabells fyllnadsgrad (λ) definieras som
kvoten mellan antalet insatta element och
antalet platser i tabellen.
– En tom tabell har λ = 0 och en full λ = 1.
• Man vet att
– Medelantalet platser som måste prövas vid en
insättning och misslyckad sökning är uppåt
begränsad av (1+1/(1- λ)2 ) /2, (λ = 0.5 ger 2.5)
– Medelantalet platser för en lyckad sökning är uppåt
begränsad av (1+1/(1- λ))/2,
(λ = 0.5 ger 1.5)
333
10
Sluten hashing, kvadratisk teknik
Exempel: Sluten hashing,
kvadratisk teknik
• Sätt in talen 89, 18, 49, 58, 9 i en tabell med 10
platser. h(x) = x % 10
• När fyllnadsgraden blir hög (>75%) blir
operationerna för den linjära tekniken
långsam.
• Kvadratisk teknik kan användas istället:
0
49
– Vid kollision tas först nästa element, sedan det
fyra platser fram sedan nio fram etc, dvs H,
H+1, H+4, …, H+i2, … där H är elementets
hashvärde.
1
2
3
4
58 9
5
6
7
8
9
18 89
89 % 10 = 9
18 % 10 = 8
49 % 10 = 9, upptagen prova H+1 = 0
58 % 10 = 8, upptagen, H+1=9 upptagen, H+4=2
9 % 10 = 9, nästa lediga plats H+4=3
334
Sluten hashing – Kvadratisk teknik
• Inte säkert att man hittar en ledig plats även om det
finns! Exempel:
– Man har en tabell som är 16 element stor och man använder
hashfunktionen h(x) = x % 16
– Stoppar in elementen 0, 16, 32 och 64. Då finns det inte plats
för några fler tal som hashas till 0. De enda platser som
kommer prövas är de upptagna 0, 1, 4 , 9.
• Men:
335
Öppen hashing
• Istället för cirkulär vektor används en vektor av
lista. I lista nummer i ligger alla element med
hashvärde i.
• Värstafallskomplexitet:
– O(n) för alla operationer (Alla element i samma lista).
• Medelfallskomplexitet:
– Om kvadratisk teknik används och tabellens storlek är ett
primtal så kan ett nytt element alltid stoppas in om
fyllnadsgraden < 0.5.
– Fullständig komplexitetsanalys saknas men i praktiken ger
den upphov till mindre klustring än linjär hashing.
336
– Insättning och misslyckad sökning blir n/tableSize
– Lyckad sökning blir (n-1)/(2*tableSize)+1
• Tumregel: Inte fler än 2*tableSize element bör
sättas in.
337
11
Från: http://www.ida.liu.se/labs/logpro/ulfni/dalg/resources/f8.pdf
Exempel: Öppen hashing
• Grundmängden 1, 2, …, 125 ska avbildas på
värdena 0, 1, ..., 6.
• Mängd = {1, 7, 27, 64, 125}
Använd hashfunktionen f(x) = x % 7.
0
1
2
Linear prob = Sluten hashing med
linjär teknik
Chaining = Öppen hashing
Double hash = Sluten hashing med
kvadratisk teknik
7
1
64
27
125
3
4
5
6
338
339
Hashfunktionen
• Vi har använt h(x) = x % m i exemplen.
– Fungerar bra om m är ett primtal.
– Annars kan periodicitet uppstå.
Föreläsning 11
• Om x tal så finns mer generella metoder, tex
Trie, Sökning och Sökträd
– h(x) = ((c1*x + c2) % p) % m
• p stort primtal > m, tex 1048583
• c1 och c2 konstant heltal > 0 och < p
• c2 ofta satt till 0
340
341
12
Innehåll
Trie
• Ytterligare en variant av träd. Vi har tidigare sett:
• Trie
• Sökning
• Sökträd
– Oordnat träd där barnen till en nod bildar en mängd
– Ordnat träd där barnen till en nod bildar en lista
• I Trie är barnen till en nod organiserade som
tabellvärden i en tabell som hör till noden.
• Trie kallas också för diskrimineringsträd, codelink tree eller radix-search tree.
342
343
Organisation av Trie
Informell specifikation
• Man når barnen (delträden) direkt genom ”namn”, dvs
argument/nycklar i barnnodens tabell.
– När man ritar träd brukar nycklarna skrivas direkt intill
motsvarande båge.
• I en trie har tabellerna en och samma nyckeltyp, till
exempel tecken.
• I många tillämpningar av Trie saknar de inre noderna
etiketter, träden är lövträd.
• Trie är normalt nedåtriktad.
• Binära träd kan ses som ett specialfall av Trie där
nyckelvärdena är left och right.
344
• Två sätt:
– Utgå från Urträdets specifikation och låt typparametern
sibling ha värdet Tabell.
• Då hanteras insättning, borttagning och värdeskoll av Tabellen själv.
• I övrigt används de vanliga operationerna för att sätta in och ta bort
barn etc.
– Sätt in lämpliga tabelloperationer direkt i specifikationen av
Trie.
• Insert-child blir tabellens Insert, Delete-child tabellens Remove och
Child tabellens Lookup.
345
13
Tillämpningar av Trie
Konstruktion av Trie
• De flesta konstruktioner av träd går bra
– Om det går bra att byta ut de delar som hanterar
barnen (till exempel som element i en lista) till att
hantera dessa som tabellvärden i en tabell.
• En länkad lista med 2-celler byts till 3-celler.
– Implementerar man tabellen som en vektor eller som
en hashtabell får man effektiva Trieimplementationer.
• Används för att konstruera Lexikon av sekvenser eller
Tabeller där nycklarna är sekvenser.
• För sekvenser med element av typ A väljer vi en Trie med
tabellnycklar av typ A.
– En sekvens motsvaras då av en väg i trädet från roten till ett löv.
– Man lägger till en slutmarkör i slutet av varje sekvens om en
sekvens kan vara början på en annan sekvens.
• En annan variant är att ha etiketter i de inre noderna också.
• Ett viktigt/vanligt specialfall är Lexikon/Tabell av
textsträng. En sträng kan ju ses som en lista eller vektor
av tecken.
346
Forts…
347
Tillämpningar
• Fördelar med att använda Trie för
Lexikon/Tabeller som lagras sekvenser som startar
med samma följd av elementvärden:
– Kompakt sätt att lagra lexikonet/tabellen på
– Sökningens tidskomplexitet proportionell mot
sekvenslängden. (En jämförelse per elementtecken)
– Den relativa komplexiteten är oberoende av
lexikonet/tabellens storlek.
• Det blir inte ”dyrare” att söka i ett stort
lexikon jämfört med ett litet.
348
• Stavningskontroll
– Skapa ett trie med alla ord som finns i språket.
• Översättningstabell
– Löven innehåller motsvarande ord i ett annat språk.
• Filsystem på Unix/PC
• Datakomprimering
– LZ78 algoritmen
– Huffman kodning
349
14
Tries för strängar
Komprimerade Tries
• Insättning
• Alla enbarnsnoder konverteras till att innehålla
hela strängen/sekvensen som är under.
• Varje intern nod i ett komprimerat Trie har två
barn och varje löv symboliserar en sträng.
• Minnesutrymmet krymper från O(m) till O(n)
– Startar i noden och går nedåt i trädet så länge det finns en matchande väg.
– När man hittar en skiljelinje, stanna och stoppa in resten av strängen som ett
delträd.
– Komprimerade tries:
• Liknande algoritm men där är löven strängar som kan måsta delas upp i två barn
• Borttagning
– I princip samma algoritm som insättning fast ”tvärtom”. Sök upp strängen som
ska tas bort och radera nerifrån i trädet upp till första förgreningen.
– m är summan av längderna av strängarna
– n är antalet strängar i strukturen
350
LZ78 eller Lempel-Ziv kodning
• Kodning:
351
LZ78 eller Lempel-Ziv kodning
• Avkodning:
– Låt frasen 0 vara strängen ””
– Skanna igenom texten
• Om du stöter på en ”ny” bokstav lägg till den på toppnivån
på trien.
• Om du stöter på en ”gammal” bokstav gå nedåt i trien tills
du inte kan matcha fler tecken, lägg till en nod i trien som
representerar den nya strängen.
• Stoppa in paret (nodeIndex, sistaBokstaven) i den
komprimerade strängen.
• Exempel:
”how now brown cow in town.”
352
– Varje gång du stöter på ”0” i strängen lägg nästa
bokstav i strängen direkt efter den föregående i den
avkodade strängen.
– För varje index < > 0 stoppa in delsträngen som
motsvaras av den noden i den avkodade strängen,
följt av nästa tecken i den komprimerade strängen.
– Notera att man inte behöver skicka med trädet.
• Exempel: 0h0o0w0_0n2w4b0r6n4c6_0i5_0t9.
353
15
Filkomprimering
Filkomprimering
• Kodningen måste ske så att man enkelt kan
avkoda strängen entydigt med kännedom om
hur de olika tecknen översätts.
• ASCII-filer är textfiler där varje bokstav
representeras av en 8-bitars ascii-kod.
– Det är alltså en fixlängdskodning
• Om man tittar på en textfil ser man att vissa
bokstäver förekommer oftare än andra.
• Om man lagrar vanligt förekommande
bokstäver med färre bitar än ovanliga så
skulle vi kunna spara utrymme.
354
Vi använder
ett trie!
– Exempel: Antag att de tre tecknen a, b och c kodas
som 0, 1 respektive 01.
• Om en mottagare får strängen 001 vad betyder det? aab
eller ac??
• Prefix-regeln: Ingen symbol kodas med en
sträng som utgör prefix till en annan symbols
kodsträng.
355
Vi vill ha optimal kompression!
• Så kort sträng som möjligt. Strängen
01011011010000101001011011010 = 29 bits
kan kortas ned till 24 bitar
001011000100001100101100 med trädet
• Bokstäverna lagras i löven.
• Den vänstra kanten betyder 0
• Den högra betyder 1
• Vad betyder
01011011010000101001011011010
356
357
16
Huffman kodning
Binärt sökträd
• Börja med en serie träd bestående av ett enda löv.
Till varje löv associeras en symbol och en
vikt = symbolens frekvens i texten som ska kodas.
• Välj de två träd som har minst vikt i roten. Bygg
ihop dem till ett träd där de blir barn till en ny
nod. Den nya noden innehåller en vikt = summan
av barnens vikter.
• Upprepa förra punkten tills vi har ett enda stort
trä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.
358
Informell specifikation
359
Varför sorterat träd?
• Skiljer sig från ett vanligt binärt träd:
– Alla noder måste ha etiketter.
• Ta bort Create, Has-Label och Set-Label och inför Make som skapar rotnod med
värde.
• Insert-operationerna utökas med ett etikettvärde.
– Man ska kunna ta bort inre noder också, inte bara löv.
• Positionsparametern i Delete-node behöver inte peka på ett löv.
• När man rycker bort en inre nod slits trädet sönder. Hur lagar man det?
• Det går snabbare att söka i strukturen…
• För binärt sökträd:
– Kolla om det sökta värdet finns i den aktuella noden.
– Om inte sök rekursivt nedåt i vänster eller höger delträd beroende på om
det sökta elementet kommer före eller efter nodens värde i
sorteringsordningen.
• Om det binära trädet är komplett:
– Är nedåtriktat
– Värstafallskomplexitet för sökning O(log n) för ett träd med n noder.
– Hur ser man till att trädet blir komplett vid insättning?
• Parent kan utelämnas.
– Eftersom trädet är sorterat kan man inte få stoppa in ett nytt element vart som
helst.
• Måste uppfylla sorteringsordningen.
360
361
17
Borttagning av nod i binärt sökträd
• Hur lagar man ett träd när man tar bort en nod mitt i?
– Om den borttagna noden bara hade ett delträd:
• Lyft upp det ett snäpp
– Om den borttagna noden hade två delträd:
• Välj noden med det minsta värdet i höger delträd som ersättning (alternativt
noden med största värdet i vänster delträd).
• Detta är standardkonstruktionen, är upp till den som
implementerar trädet.
– De vanligaste tillämpningarna är inte beroende av denna detalj.
– Viktigt att visar sitt beslut i specifikation och dokumentation.
Varför inte ändra gränsytan?
• Eftersom man inte får sätta in ett nytt element vart som
helst så kanske man lika gärna kan ersätta insert-left och
insert-right med en metod place som automatiskt
placerar det rätt?
• På samma sätt ersätta delete-node(pos, bt) med
remove(val, bt)?
• Bägge dessa metoder ligger på en högre
abstraktionsnivå än övriga metoder i gränsytan.
– Place implementerar man i huvudsak med hjälp av andra
metoder i gränsytan vilket är lite märkligt.
– Strukturen döljs (för oss) och blir mer lik en mängd.
362
Tillämpningar av Binärt sökträd
363
Generaliseringar
C
N
R
S
T
U
Y
• Ett binärt sökträd underlättar sökning i en endimensionell datamängd.
• Lätt att generalisera detta till sökning i en 2dimensionell datamängd (quadtree) eller så hög
dimension man önskar (tex octtree).
• Framför allt till konstruktioner av Lexikon
och Tabell.
• Notera att inorder-traversering av binärt
sökträd ger en sorterad sekvens av de
ingående elementen.
– Kan alltså sortera element genom att stoppa in
dem ett och ett i ett tomt Binärt sökträd och
sedan traversera det.
364
365
18
Quadtree (Fyrträd)
Quadtree – forts.
• Organiserat som ett binärt träd med
förgreningsfaktor 4 istället för 2.
• Tolkning (vanligast):
• Man kan också använda det för att representera kurvor och ytor.
– Svarta kvadranter – fylls helt av objektet
– Grå kvadranter – fylls delvis av objektet
– Vita kvadranter – innehåller inte objektet
– Rotnoden delar in den givna ytan (oftast kvadrat) i
fyra lika stora kvadrater. Vart och ett av de fyra
barnen delar i sin tur sin kvadrat i fyra osv. Inga
koordinater behöver lagras i inre noder.
• Exempel på tillämpning
– GIS
– qtdemo i Matlab på peppar…
• Kan användas för att lagra lägesinformation
för punktformiga grafiska objekt.
366
367
19