Tilbake
3.5

3.5 Køer, stakker, amortisert analyse og disjunkte mengder

FIFO-kø med wraparound (håndkjøring), stakk (LIFO), `Table-Insert` (amortisert `O(1)`) og disjunkte mengder (`Union-Find`) — de øvrige strukturene.

55 min
8 oppgaver
Køerstakkeramortisert analysedisjunkte mengder
Din fremgang i kapitlet
0 / 8 oppgaver

Forkunnskaper

- kap. 3.1 — hauger. Dette sto der: en datastruktur kan
bo i et array A[1..n] med indeks fra 1, og operasjonene beskrives som
indeksregning. Vi bruker den samme konvensjonen her.
- kap. 1.1 — de asymptotiske symbolene. Amortisert
analyse handler om å skille kostnaden til én operasjon fra
gjennomsnittet over en hel serie, og det krever presis notasjon.
- kap. 2.1 — «forventet» mot «garantert». Amortisert er
en tredje ting igjen: det er et gjennomsnitt over en serie operasjoner, uten
noen sannsynlighet inne i bildet.

Ingen av delene krever noe utover dette.

Notasjons- og pseudokodeliste

FIFO-køen, wraparound og de døde cellene (~16 min)

I kantinekøen står den som kom først, fremst. Nye gjester stiller seg bakerst,
og det er alltid den fremste som blir betjent. Det er en FIFO-køfirst
in, first out
.

Skal køen ligge i et array med fast lengde, oppstår et praktisk problem. Etter
noen inn- og utmeldinger har hele køen «vandret» bakover i arrayet, og til slutt
er den bakerste plassen brukt opp — selv om det er masse ledig plass foran.
Løsningen er å la køen gå rundt hjørnet: når indeksen når enden, begynner
den på 1 igjen. Det kalles wraparound, og det er derfor køen kalles
sirkulær.

FIFO-kø

en FIFO-kø (first in, first out) er en samling der elementer tas ut i
den rekkefølgen de ble lagt inn.

Enqueue legger til bakerst, Dequeue tar ut forrest. Begge er Θ(1)\Theta(1).

Rekkefølgen er hele poenget. En stakk gjør det motsatte — den tar ut det
sist innlagte først.

`head` og `tail`
Q.head er indeksen til det fremste elementet, altså det som tas ut ved
neste Dequeue. Q.tail er indeksen der det neste elementet skal
skrives inn.

Køen er tom når head == tail, og den inneholder elementene fra head og
rundt til, men ikke med, tail.

Å bytte om de to er den vanligste feilen i denne håndkjøringen. Huskeregel:
head er der du spiser fra, tail er der du legger på.

Wraparound og døde celler
Wraparound er regelen om at en indeks som når n, settes til 1 ved neste
økning — arrayet leses som en sirkel.

En død celle er en celle som ligger utenfor det logiske intervallet fra
head til tail, men som fortsatt inneholder en gammel verdi. Dequeue
sletter ikke noe; den flytter bare head.

Døde celler skal med i svaret. Svarformatet for en kø-håndkjøring er
hele tabellen slik den faktisk står i minnet, pluss head og tail.

📜Pseudokode-kontrakt: `Enqueue` og `Dequeue`
Antagelser om representasjon. Køen ligger i Q[1..n], indeks fra 1. Ved
oppstart er Q.head = Q.tail = 1. Cellene som ikke er i bruk, beholder de
verdiene de måtte ha fra før — ingenting nullstilles.

Prebetingelse: for Enqueue at køen ikke er full; for Dequeue at den
ikke er tom.
Postbetingelse: Enqueue har lagt x bakerst; Dequeue har returnert det
fremste elementet og flyttet head.

Enqueue(Q, x)
  Input:  koeen Q og et element x
  Output: x lagt bakerst i koeen
  Q[Q.tail] = x
  if Q.tail == Q.length
      Q.tail = 1
  else
      Q.tail = Q.tail + 1

Dequeue(Q)
  Input:  koeen Q
  Output: det fremste elementet
  x = Q[Q.head]
  if Q.head == Q.length
      Q.head = 1
  else
      Q.head = Q.head + 1
  return x
  Kjoeretid: Theta(1) for begge

Invarianten i én setning: de elementene som faktisk står i køen, er de som
ligger fra indeks head og framover — rundt hjørnet om nødvendig — til, men
ikke med, indeks tail.

Legg merke til at Dequeue ikke sletter noe. Verdien blir liggende igjen i
cellen, og den kan bli overskrevet senere av en Enqueue som kommer rundt.
Det er nettopp disse restene som er de døde cellene.

Kjøretid: Θ(1)\Theta(1) for begge — noen få indeksregninger, uansett hvor
mange elementer køen inneholder.

✏️Eksempel 1: Ni operasjoner på en kø med fem plasser

En vaktsentral bruker en sirkulær FIFO-kø Q[1..5] til innkommende meldinger.
Ved oppstart er Q.head = Q.tail = 1, og alle cellene er tomme.

Utfør: Enqueue(a), Enqueue(b), Enqueue(c), Enqueue(d), Dequeue,
Dequeue, Dequeue, Enqueue(e), Enqueue(f).

Oppgi hele tabellen til slutt, sammen med head og tail.

En understrek betyr at cellen aldri har vært skrevet i.

StegOperasjonHva som skjerQ[1..5] etterpåheadtail
1Enqueue(a)skriver a i Q[1]a, _, _, _, _12
2Enqueue(b)skriver b i Q[2]a, b, _, _, _13
3Enqueue(c)skriver c i Q[3]a, b, c, _, _14
4Enqueue(d)skriver d i Q[4]a, b, c, d, _15
5Dequeuereturnerer a fra Q[1]a, b, c, d, _25
6Dequeuereturnerer b fra Q[2]a, b, c, d, _35
7Dequeuereturnerer c fra Q[3]a, b, c, d, _45
8Enqueue(e)skriver e i Q[5]a, b, c, d, e41
9Enqueue(f)skriver f i Q[1]f, b, c, d, e42

Sluttilstanden — det du ville levert på eksamen:
Tabell: f, b, c, d, e, head = 4,
tail = 2.

Tre ting er verdt å studere i tavlen.
For det første: de tre Dequeue-operasjonene endrer ikke tabellen i det
hele tatt. Bare head flytter seg. Verdiene a, b og c blir liggende.
For det andre: steg 8 skriver e i Q[5], og siden tail da sto på 5, går
den rundt hjørnet til 1. Steg 9 skriver derfor f over den døde verdien a.
For det tredje: den logiske køen til slutt er
d, e, f — les fra head = 4

og rundt til tail = 2. Men b og c står fortsatt i

tabellen som døde celler, og de skal med i svaret.
Fellen her er #3 — køfeil ved håndkjøring: å levere bare den logiske køen

d, e, f i stedet for hele tabellen, eller å bytte

om head og tail. Delvis riktig svar gir delvis uttelling, og det siste
poenget henger på at både tabellen og begge indeksene er med.

📝Oppgave 1

(Innstegsoppgave, sjanger D — definisjon med egne ord, altså én presis setning
med hovedpoenget først.)

Forklar hva head og tail peker på i en sirkulær FIFO-kø, og hva som menes
med en «død celle».

📝Oppgave 2
Eksamensnivå, sjanger C

En sirkulær FIFO-kø Q[1..4] starter tom med head = tail = 1.

Utfør: Enqueue(p), Enqueue(q), Dequeue, Enqueue(r), Enqueue(s),
Dequeue, Enqueue(t).

Oppgi hele tabellen, head og tail.

📝Oppgave 3
Eksamensnivå, sjanger F

Ta stilling til hver av påstandene om en sirkulær FIFO-kø Q[1..n]:

a) Køen er tom når head == tail.
b) Dequeue nullstiller cellen den leser fra.
c) Enqueue og Dequeue er O(lgn)O(\lg n).

Stakken (~7 min)

Stakken er køens speilbilde. Der køen tar ut det eldste, tar stakken ut det
nyeste — som en bunke tallerkener der du både legger på og tar av på toppen.

📜Pseudokode-kontrakt: `Push` og `Pop`
Antagelser om representasjon. Stakken ligger i S[1..n], indeks fra 1.
S.top er indeksen til det øverste elementet, og S.top = 0 betyr tom stakk.

Prebetingelse: for Push at stakken ikke er full; for Pop at den ikke er
tom.
Postbetingelse: Push har lagt x øverst; Pop har returnert og fjernet
det øverste elementet.

Push(S, x)
  Input:  stakken S og et element x
  Output: x lagt oeverst
  S.top = S.top + 1
  S[S.top] = x

Pop(S)
  Input:  stakken S
  Output: det oeverste elementet
  S.top = S.top - 1
  return S[S.top + 1]
  Kjoeretid: Theta(1) for begge

Invarianten i én setning: elementene i stakken er S[1..S.top], med det
sist innlagte øverst.

Ingen wraparound trengs. Stakken vokser og krymper i samme ende, så den
vandrer aldri gjennom arrayet slik køen gjør. Det er derfor
stakk-håndkjøringer er enklere enn kø-håndkjøringer — og derfor køen er den
som kommer på eksamen.

Kjøretid: Θ(1)\Theta(1) for begge.

📝Oppgave 4
Sjanger C

En stakk S[1..5] starter tom med S.top = 0.

Utfør: Push(4), Push(9), Push(2), Pop, Push(7), Pop, Push(5).

Oppgi S[1..S.top] og verdien til S.top til slutt, samt hvilke verdier
Pop returnerte.

Amortisert analyse og Table-Insert (~15 min)

En dynamisk tabell er et array som vokser etter behov. Så lenge det er plass,
koster en innsetting konstant tid. Blir tabellen full, må den byttes ut med en
dobbelt så stor, og alt kopieres over — en enkeltoperasjon som koster
Θ(n)\Theta(n).

Spørsmålet er hva en innsetting «koster» når noen få av dem er så dyre. Svaret
er ikke verste tilfelle for én operasjon, og det er heller ikke et
sannsynlighetsregnestykke. Det er en tredje ting: amortisert kostnad.

Amortisert kjøretid
Amortisert kjøretid er den gjennomsnittlige kostnaden per operasjon over en
hel serie operasjoner, i verste tilfelle for serien.

Det er ikke en forventning: ingen sannsynlighet er involvert. Garantien
gjelder totalen for serien, ikke for den enkelte operasjonen.

Sagt konkret: at Table-Insert er amortisert O(1)O(1) betyr at nn
innsettinger til sammen koster O(n)O(n) — selv om enkelte av dem koster
Θ(n)\Theta(n) hver for seg.

📜Pseudokode-kontrakt: `Table-Insert`
Antagelser om representasjon. Tabellen T har feltene T.num (antall
elementer) og T.size (kapasitet). Ved oppstart er begge 0. Å allokere en ny
tabell og kopiere over T.num elementer koster Θ(T.num)\Theta(T.num).

Prebetingelse: ingen.
Postbetingelse: x ligger i tabellen, og T.num er økt med 1.

Table-Insert(T, x)
  Input:  den dynamiske tabellen T og et element x
  Output: T med x satt inn
  if T.size == 0
      alloker T med plass til 1 element
      T.size = 1
  if T.num == T.size
      alloker en ny tabell med plass til 2 * T.size
      kopier alle T.num elementer over
      T.size = 2 * T.size
  sett x inn i T
  T.num = T.num + 1
  Kjoeretid: Theta(n) i verste tilfelle, amortisert O(1)

Grunnideen i én setning: en dobling er dyr, men den kjøper plass til like
mange billige innsettinger som det nettopp ble kopiert elementer — så
kostnaden fordeles utover.

Utledningen, ledd for ledd. Kopieringer skjer ved innsetting nummer 1,2,4,8,1, 2, 4, 8, \dots, altså ved hver toerpotens opp til nn.

Intuisjon: det er de eneste øyeblikkene tabellen er full.

Kopieringen ved innsetting nummer 2j2^j koster 2j12^{j-1} (antall elementer som
flyttes). Summen av alle kopieringene er derfor

1+2+4++2lgn<2n1 + 2 + 4 + \dots + 2^{\lfloor \lg n\rfloor} < 2n

Intuisjon: den geometriske summen domineres av det siste leddet — all
kopiering til sammen koster mindre enn to ganger den siste kopieringen. Det er
den samme egenskapen som gjør at 1+12+14+<21 + \tfrac12 + \tfrac14 + \dots < 2.

I tillegg koster hver av de nn innsettingene selv Θ(1)\Theta(1). Totalen blir
under 3n3n, altså O(n)O(n).

Intuisjon: deler du O(n)O(n)nn operasjoner, får du O(1)O(1) per operasjon.

Kjøretid: Θ(n)\Theta(n) i verste tilfelle for én innsetting, men
amortisert O(1)O(1) over en serie.

✏️Eksempel 2: Kostnaden for de seksten første innsettingene

En dynamisk tabell starter tom. Sett inn seksten elementer med Table-Insert,
og regn ut kostnaden per innsetting og totalen.

Kostnaden for en innsetting er 1 hvis det er plass, og 1+1 + antall elementer
som må kopieres hvis tabellen må dobles.

Innsetting nr.Kapasitet etterpåKostnadSum så langt
1111
2223
3436
4417
58512
68113
78114
88115
916924
1016125
1116126
1216127
1316128
1416129
1516130
1616131

Totalen for 16 innsettinger: 31 enheter, altså
1,94 per innsetting i snitt.
Det avgjørende er at snittet ikke vokser. De dyre radene er få og ligger
langt fra hverandre: 1, 2, 3, 5, 9, 17 og så videre — én for hver toerpotens.
Mellom dem ligger stadig lengre strekk med kostnad 1.
Regn etter: kopieringene koster 1+2+4+8=151 + 2 + 4 + 8 = 15 til sammen, altså under
2n=322n = 32. Legger vi til de 16 enkeltinnsettingene og de to første
allokeringene, ender vi under 3n=483n = 48.
Svarformen på eksamen: «amortisert O(1)O(1), fordi doblingene koster under
2n2n til sammen». To linjer. Ikke hele tabellen, med mindre den er etterspurt.
📝Oppgave 5
Eksamensnivå, sjanger F

En kandidat skriver: «Table-Insert er Θ(n)\Theta(n) per innsetting, siden
tabellen må kopieres.»

Er utsagnet riktig? Svar ja eller nei, og gi den presise formuleringen.

📝Oppgave 6
Eksamensnivå, sjanger F…

En kollega foreslår å la tabellen vokse med ett element om gangen i stedet
for å doble: hver gang den er full, allokeres en ny tabell med plass til
T.size + 1 elementer.

a) Hva blir den totale kostnaden for nn innsettinger?
b) Hva blir den amortiserte kostnaden per innsetting?
c) Hva er lærdommen?

Disjunkte mengder og Union-Find (~17 min)

Et veinett bygges ut bit for bit. Etter hver nye veistrekning vil du vite: er
disse to stedene nå knyttet sammen, direkte eller indirekte? Og: hører disse to
til samme sammenhengende område?

Strukturen som svarer på slike spørsmål heter disjunkte mengder, ofte kalt
Union-Find. Den holder styr på en samling mengder som ikke overlapper, og
støtter to ting: slå sammen to mengder, og finne ut hvilken mengde et element
hører til.

Det er nøyaktig det MST-Kruskal trenger i kap. 4.2:
en kant kan legges til spenntreet bare hvis endepunktene ligger i ulike
komponenter.

Disjunkte mengder

en samling mengder der ingen to mengder har noe element felles.

Hver mengde har en utpekt representant — ett bestemt element som står for
hele mengden. To elementer ligger i samme mengde nøyaktig når Find-Set gir
samme representant for begge.

Representanten kan skifte når to mengder slås sammen, men så lenge ingenting
endres, er den den samme hver gang du spør.

Skogrepresentasjonen

hver mengde lagres som et tre: hver node har en peker x.p til
forelderen, og rota peker på seg selv. Rota er mengdens representant.

Find-Set(x) følger p-pekerne oppover til den når rota.

Trærne her har ingenting med søketrær å gjøre. Det finnes ingen ordning
mellom nodene — pekerne sier bare «tilhører samme mengde som».

Rangheuristikken
x.rank er en øvre grense for høyden på deltreet med rot x. Ved Union
henges rota med lavest rang under den med høyest.

Er rangene like, velges den ene som ny rot og får rangen sin økt med 1.

Effekten er at trærne holder seg lave: høyden blir O(lgn)O(\lg n), og dermed er
både Find-Set og Union O(lgn)O(\lg n). Uten heuristikken kan et tre bli en
kjede med høyde n1n-1.

📜Pseudokode-kontrakt: `Make-Set`, `Find-Set`, `Union` og `Link`
Antagelser om representasjon. Hvert element x har feltene x.p
(forelder) og x.rank. Rota r har r.p = r. Denne boka bruker
rangheuristikken, men ikke stikomprimering.

Prebetingelse: for Make-Set at x ikke allerede ligger i en mengde; for
Union at begge argumentene ligger i en mengde.
Postbetingelse: Find-Set(x) returnerer representanten for mengden x
ligger i. Union(x, y) gjør at x og y etterpå ligger i samme mengde.

Make-Set(x)
  x.p = x
  x.rank = 0

Find-Set(x)
  while x.p != x
      x = x.p
  return x

Link(x, y)
  Input:  to ROETTER x og y
  if x.rank > y.rank
      y.p = x
  else
      x.p = y
      if x.rank == y.rank
          y.rank = y.rank + 1

Union(x, y)
  Link(Find-Set(x), Find-Set(y))
  Kjoeretid: Theta(1) for Make-Set, O(lg n) for Find-Set og Union

Invarianten i én setning: hver mengde er et tre der alle p-pekere fører
opp til representanten, og x.rank er alltid minst like stor som høyden på
deltreet under x.

Legg merke til at Link tar to røtter, ikke to vilkårlige elementer.
Union gjør oppslaget først. En vanlig håndkjøringsfeil er å henge et element
midt i treet under en annen rot.

Kjøretid: Make-Set er Θ(1)\Theta(1). Find-Set følger én sti opp til rota,
og med rangheuristikken er høyden O(lgn)O(\lg n) — derav O(lgn)O(\lg n) for både
Find-Set og Union.

✏️Eksempel 3: Fire sammenslåinger med rangheuristikk

Seks målestasjoner a, b, c, d, e, f starter hver for seg:
Make-Set kalles på alle seks, så alle har rang 0 og er sin egen rot.

Utfør Union(a, b), Union(c, d), Union(b, d) og Union(e, f).

Oppgi hvilke mengder som finnes til slutt, hvem som er representant for hver,
og hva Find-Set(a) returnerer.

StegOperasjonHva som skjerMengdene etterpå
1Union(a, b)like ranger (0): rot a henges under rot b, og b får rang 1{a, b} med rot b; {c} med rot c; {d} med rot d; {e} med rot e; {f} med rot f
2Union(c, d)like ranger (0): rot c henges under rot d, og d får rang 1{a, b} med rot b; {c, d} med rot d; {e} med rot e; {f} med rot f
3Union(b, d)like ranger (1): rot b henges under rot d, og d får rang 2{a, b, c, d} med rot d; {e} med rot e; {f} med rot f
4Union(e, f)like ranger (0): rot e henges under rot f, og f får rang 1{a, b, c, d} med rot d; {e, f} med rot f

Sluttilstanden — det du ville levert på eksamen:
Mengdene er {a, b, c, d} med representant d, og {e, f} med representant
f. Find-Set(a) returnerer d.

Slik ser skogen ut:
        d (rang 2)                f (rang 1)
       /         \                   |
   b (rang 1)  c (rang 0)        e (rang 0)
      |
   a (rang 0)

Legg merke til hvordan rangene styrer. I steg 1 og 2 møtes to røtter med

rang 0, og den ene får rang 1. I steg 3 møtes to røtter med rang 1 — igjen
uavgjort, så den ene blir rot og får rang 2. Hadde rangene vært ulike, ville

den lavere rota alltid havnet under, og ingen rang ville økt.

Find-Set(a) følger to pekere: fra a til b, fra b til d. Det er

høyden på treet, og det er nettopp den rangheuristikken holder nede.

📝Oppgave 7
Eksamensnivå, sjanger C

Fem noder u, v, w, x, y får hver sin Make-Set.

Utfør Union(u, v), Union(w, x), Union(v, w), Union(x, y).

a) Hvilke mengder finnes til slutt, og hvem er representanten?
b) Hvor mange pekere følger Find-Set(u)?

📝Oppgave 8
Eksamensnivå, sjanger H

Et fiberselskap legger nye kabler mellom nn kundesteder, én strekning om
gangen, til sammen mm strekninger. Etter hver ny strekning vil de vite hvor
mange sammenhengende områder nettet består av.

Beskriv en algoritme, og oppgi kjøretiden.

Kjøretidene samlet

Dette er kapitlets puggeflate. Eksamen er hjelpemiddelfri, så tabellen må ligge
i hodet.

OperasjonKjøretidKrav / egenskap
Enqueue, DequeueΘ(1)\Theta(1)sirkulært array; Dequeue sletter ikke, den flytter head
Push, PopΘ(1)\Theta(1)LIFO; ingen wraparound nødvendig
Table-InsertΘ(n)\Theta(n) verste for én, amortisert O(1)O(1)doblingsstrategi; fast økning gir amortisert Θ(n)\Theta(n)
Make-SetΘ(1)\Theta(1)oppretter et enkeltnode-tre med rang 0
Find-SetO(lgn)O(\lg n)med rangheuristikk; følger én sti opp til rota
UnionO(lgn)O(\lg n)to Find-Set pluss ett Link
nn Table-Insert til sammenO(n)O(n)dette er hele poenget med «amortisert»

Én presisering som er verdt å ta med seg. De tre delene av dette kapitlet
har hver sin rolle på eksamen: køen kommer som håndkjøring, amortisert analyse
som definisjon, og Union-Find som verktøy i MST-Kruskal. Ingen av dem er
tunge, men alle tre har en presis svarform.

Begrepsbank

Begrepsbanken er flashcard- og repetisjonsstoff — den gjentar det du nettopp
har lest. Hopp trygt over ved førstegangslesing; tidsanslaget for kapitlet
gjelder kjernestoffet.

FIFO-kø

en samling der elementer tas ut i den rekkefølgen de ble lagt inn — first in,
first out
.

Enqueue legger til bakerst, Dequeue tar ut forrest, begge i Θ(1)\Theta(1).

Brukes i BFS (se kap. 4.1), der køen er det som
gjør at søket går lag for lag.

Stakk

en samling der elementer tas ut i motsatt rekkefølge av innleggingen — last
in, first out
.

Push og Pop arbeider begge på toppen, i Θ(1)\Theta(1).

Brukes implisitt i DFS gjennom rekursjonen, og trenger ingen
wraparound.

`head` og `tail`
head er indeksen til det fremste elementet i køen; tail er indeksen der
neste element skal skrives.

Køen er tom når head == tail.

Å bytte om de to er den vanligste håndkjøringsfeilen i denne strukturen —
felle #3.

Wraparound

regelen om at en indeks som når n, settes til 1 ved neste økning, slik at
arrayet leses som en sirkel.

Gjelder både head og tail.

Uten wraparound går køen tom for plass selv om det er ledige celler foran
den.

Død celle

en celle som fortsatt inneholder en gammel verdi, men som ligger utenfor
intervallet fra head til tail.

Oppstår fordi Dequeue bare flytter head og aldri sletter.

Døde celler skal med i svaret på en kø-håndkjøring — svarformatet er hele
tabellen.

Amortisert kjøretid

gjennomsnittlig kostnad per operasjon over en hel serie, i verste tilfelle for
serien.

Ingen sannsynlighet er involvert — dette er ikke det samme som «forventet».

Table-Insert er amortisert O(1)O(1): nn innsettinger koster O(n)O(n) til
sammen, selv om enkelte koster Θ(n)\Theta(n).

`Table-Insert`

setter inn ett element i en dynamisk tabell, og dobler kapasiteten når tabellen
er full.

Kjøretid Θ(n)\Theta(n) i verste tilfelle for én innsetting, amortisert O(1)O(1)
over en serie.

Doblingen er avgjørende. Med en fast økning på ett element blir amortisert
kostnad Θ(n)\Theta(n).

Aggregert analyse

metoden der du regner ut totalkostnaden for en serie på nn operasjoner og
deler på nn.

For Table-Insert: kopieringene summerer seg til under 2n2n, pluss nn
enkeltinnsettinger, altså under 3n3n totalt.

Det er den enkleste av de amortiserte metodene, og den holder for alt i
denne boka.

Doblingsstrategien

å multiplisere kapasiteten med en faktor c>1c > 1 når tabellen blir full.

Gir amortisert O(1)O(1) per innsetting, fordi kopieringene blir eksponentielt
sjeldnere.

Enhver additiv økning — «legg til kk plasser» — gir derimot amortisert
Θ(n)\Theta(n).

Disjunkte mengder

en samling mengder uten felles elementer, der hver mengde har en representant.

Operasjonene er Make-Set, Find-Set og Union.

To elementer ligger i samme mengde nøyaktig når Find-Set gir samme
representant for begge.

`Make-Set`

oppretter en ny mengde som bare inneholder x, ved å sette x.p = x og
x.rank = 0.

Kjøretid Θ(1)\Theta(1).

Kalles én gang per element før noen sammenslåing.

`Find-Set`

følger p-pekerne fra x opp til rota, og returnerer rota.

Kjøretid O(lgn)O(\lg n) med rangheuristikk.

Rota er representanten. Endres ingenting mellom to kall, gir de samme
svar.

`Union` og `Link`
Union(x, y) slår sammen mengdene ved å kalle Link på de to røttene.
Link henger rota med lavest rang under den med høyest.

Kjøretid O(lgn)O(\lg n), dominert av de to Find-Set-kallene.

Link tar røtter, ikke vilkårlige elementer — en vanlig
håndkjøringsfeil.

Rang
x.rank er en øvre grense for høyden på deltreet med rot x.

Økes bare når to røtter med lik rang slås sammen.

Heuristikken holder trærne lave, slik at høyden blir O(lgn)O(\lg n) i stedet
for n1n-1.

Skogrepresentasjon

hver disjunkt mengde lagres som et tre av forelderpekere, der rota peker på seg
selv.

Det finnes ingen ordning mellom nodene — pekerne betyr bare «tilhører samme
mengde».

Ikke forveksle med binære søketrær. Antall barn er ubegrenset, og
nøkkelverdiene spiller ingen rolle.

Sjanger C — håndkjøring

oppgavetypen der du utfører operasjonene steg for steg og oppgir
sluttilstanden.

For en kø er svarformen hele tabellen inkludert døde celler, pluss head og
tail
. For Union-Find er den hvilke mengder som finnes og hvem som er
representant.

Delvis riktig gir delvis uttelling — skriv ned tilstanden underveis.

Sjanger D — definisjon med egne ord

oppgavetypen der du forklarer et begrep presist og kort.

Svarformen er én til to setninger med hovedpoenget først.

«Hva betyr amortisert kjøretid?» er den typiske definisjonsoppgaven fra
dette kapitlet.

Repetisjonsoppgaver

Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.

Skolesaga er en uavhengig læringsressurs og er ikke tilknyttet eller godkjent av Norges teknisk-naturvitenskapelige universitet. Dette er ikke offisielt studiemateriell. Les mer.