Tilbake
3.1

3.1 Hashtabeller og lukket hashing (linear probing)

Hashtabellen med lukket hashing og lineær probing — hashfunksjon, kollisjonshåndtering og håndkjøring av innsetting i array-form.

55 min
7 oppgaver
Hashtabellerlukket hashing (linear probing)
Din fremgang i kapitlet
0 / 7 oppgaver

Forkunnskaper

- kap. 1.2 — løkketelling. Kjøretidene her leses ut av
hvor mange plasser en probing-løkke må innom.
- kap. 1.1 — forskjellen mellom forventet og verste
tilfelle. Hashing er hele fagets tydeligste eksempel på at de to er forskjellige.

Er array-tenkning ferskt:

- Lister — indeksering fra 0, som er alt hashtabellen er
bygget på.
- Ordbøker — den samme strukturen sett fra brukersiden. Det
er maskineriet under den du møter her.

Notasjons- og pseudokodeliste

Løkke 1 — garderoben med nummererte knagger (ca. 12 min)

Se for deg en garderobe med sju knagger, nummerert 0 til 6. Du får en jakke med et
lappnummer, for eksempel 15, og skal henge den et sted du selv kan finne den
igjen
uten å lete gjennom hele rekka.

Trikset er å regne ut knaggen fra lappnummeret: ta resten når du deler på 7.
15=27+115 = 2 \cdot 7 + 1, så jakke 15 skal på knagg 1. Kommer du tilbake senere,
regner du ut det samme og går rett til knagg 1. Ingen leting.

Det er en hashtabell. Regnestykket «rest ved divisjon med tabellstørrelsen»
er hashfunksjonen, og den skrives h(k)=kmodNh(k) = k \bmod N.

Så oppstår problemet som hele resten av kapitlet handler om: neste jakke har
lappnummer 22, og 22=37+122 = 3 \cdot 7 + 1 — også knagg 1. To jakker, én knagg. Det
kalles en kollisjon, og måten IN2010 løser den på, er den enkleste som
finnes: heng jakka på neste ledige knagg.

Hashtabell

Et array med NN plasser der en nøkkel kk lagres på en indeks som regnes ut
direkte fra nøkkelen selv, ved hjelp av en hashfunksjon.

Fordelen er at du finner igjen en nøkkel uten å lete: du regner ut samme indeks på
nytt. Oppslag, innsetting og medlemskapstest er derfor O(1)O(1) forventet. Verste
tilfelle er O(n)O(n), når mange nøkler har havnet på samme sted.

Hashfunksjon
Funksjonen som gjør en nøkkel om til en indeks i tabellen. I IN2010 er den alltid

h(k)=kmodN,h(k) = k \bmod N,

altså resten når kk deles på tabellstørrelsen NN.

Resten ved divisjon med NN ligger alltid mellom 00 og N1N-1, og det er nettopp
derfor funksjonen alltid gir en gyldig indeks. En god hashfunksjon sprer nøklene
jevnt utover; en dårlig samler dem på få plasser.

Kollisjon

To ulike nøkler som gir samme hashverdi: h(k1)=h(k2)h(k_1) = h(k_2) selv om
k1k2k_1 \ne k_2.

Kollisjoner er uunngåelige — med NN plasser og flere enn NN mulige nøkler må
noen dele plass. Spørsmålet er aldri om de oppstår, men hvordan de håndteres.

Lukket hashing med lineær probing

Kollisjonsstrategien IN2010 bruker: alle nøkler ligger i selve arrayet, og
er plassen opptatt, prøver du neste plass — (i+1)modN(i+1) \bmod N — til du finner en
ledig.

«Lukket» betyr at ingenting lagres utenfor tabellen. «Lineær probing» betyr at du
prøver plassene ett hakk om gangen. Alternativet, der hver plass har en lenket
liste, heter åpen hashing eller kjeding og er ikke det som håndkjøres på eksamen
her.

📜Pseudokode-kontrakt: `Insert`
Antagelser om representasjon. T er et array med N plasser indeksert fra 0.
En plass er enten tom eller inneholder én nøkkel. Nøklene er heltall, og
hashfunksjonen er h(k) = k mod N. Vi antar at tabellen ikke er full — er den
det, må den utvides først, se kap. 3.2.

Prebetingelse: minst én plass i T er tom, eller k finnes allerede i T.
Postbetingelse: k finnes i T nøyaktig én gang, og alle andre nøkler står
uendret.

Procedure Insert(T, k)
  Input:  hashtabell T som array med N plasser (indeks fra 0), noekkel k
  Output: T med k satt inn; lik noekkel overskrives i stedet for aa dupliseres
  N = T.length
  i = k mod N
  while T[i] er ikke tom and T[i] er ulik k:
      i = (i + 1) mod N
  T[i] = k

Grunnideen i én setning: søket etter en ledig plass starter alltid på h(k)h(k)
og går framover i faste steg, så den samme sekvensen kan følges på nytt når
nøkkelen skal finnes igjen.

De to detaljene sensor trekker for, og de er begge i while-linja:

1. mod N. Uten den vil i = i + 1 løpe utenfor 0..N-1 så snart en klynge
når enden av arrayet. Dette er felle #11 i bokas feilregister — probing som
går utenfor tabellen, eller som ikke overskriver en lik nøkkel.
2. and T[i] er ulik k. Uten den stopper løkka først på en tom plass, og en
nøkkel som allerede finnes, blir lagt inn en gang til. En hashtabell skal ha
hver nøkkel én gang.

Kjøretid: O(1)O(1) forventet — sprer hashfunksjonen nøklene jevnt, er
probing-sekvensen kort. O(n)O(n) i verste tilfelle, når alle nøklene har havnet i
én sammenhengende klynge og løkka må gå gjennom hele. Å oppgi O(1)O(1) uten ordet
«forventet» er en fast trekkgrunn.

✏️Eksempel 1: Innsetting med klyngedannelse

Sett inn nøklene 15, 22, 8, 29 og 3 i denne rekkefølgen i en tom hashtabell med
N=7N = 7 og h(k)=kmod7h(k) = k \bmod 7, med lineær probing. Oppgi hele tabellen.

Regn ut h(k)h(k) for hver nøkkel først: 15mod7=115 \bmod 7 = 1, 22mod7=122 \bmod 7 = 1,
8mod7=18 \bmod 7 = 1, 29mod7=129 \bmod 7 = 1, 3mod7=33 \bmod 7 = 3.

Fire av fem nøkler starter på samme plass. Det er nettopp poenget med eksempelet.

StegNøkkel kkh(k)h(k)Prøvde indekserTabell etter steget
11511_, 15, _, _, _, _, _
22211 -> 2_, 15, 22, _, _, _, _
3811 -> 2 -> 3_, 15, 22, 8, _, _, _
42911 -> 2 -> 3 -> 4_, 15, 22, 8, 29, _, _
5333 -> 4 -> 5_, 15, 22, 8, 29, 3, _

Sluttilstand — dette er svaret du leverer:
indeks:   0    1    2    3    4    5    6
T:        _    15   22   8    29   3    _
Eller på én linje: _, 15, 22, 8, 29, 3, _
Legg merke til steg 5. Nøkkelen 3 hasher til plass 3, som er sin egen
riktige plass — men den er opptatt av 8, som havnet der på grunn av en helt annen
kollisjon. Klyngen som de fire første nøklene bygget, sperrer nå for en nøkkel som

ikke hadde noe med dem å gjøre. Det heter primær klyngedannelse, og det er

lineær probings viktigste svakhet.

Totalt antall prøver: 1+2+3+4+3=131 + 2 + 3 + 4 + 3 = 13, altså 2,6 per innsetting. På

en tom tabell hadde det vært 1 per innsetting. Det er slik en klynge koster.
Fellenote. Fella her er felle #11 — å levere bare de innsatte nøklene i
stedet for hele tabellen. Sensor ber om hele tabellen, med _ for tomme
plasser, fordi det er de tomme plassene som viser at du har fulgt
probing-sekvensen riktig.

📝Oppgave 1

(Innstegsoppgave, sjanger E — håndkjøring, altså at du utfører operasjonen steg
for steg og oppgir sluttilstanden.) En hashtabell har N=5N = 5 og
h(k)=kmod5h(k) = k \bmod 5.

a) Regn ut h(k)h(k) for nøklene 11, 6, 7 og 20.
b) Sett dem inn i den rekkefølgen, med lineær probing, og oppgi hele
tabellen.

Løkke 2 — wraparound: når klyngen når enden (ca. 13 min)

— naturlig pausepunkt —

Nå kommer detaljen som skiller en riktig håndkjøring fra en gal, og den er verdt
et helt poeng i hvert eneste sett.

Hva skjer når en nøkkel hasher til den siste plassen i tabellen, og den er
opptatt?

Svaret er ikke «plass NN» — den finnes ikke. Svaret er at du går rundt til
plass 0. Det er derfor probing-steget skrives

ineste=(i+1)modNi_{\text{neste}} = (i + 1) \bmod N

og ikke bare i+1i + 1. Tabellen er en ring, ikke en linje.

Uten mod N går indeksen utenfor arrayet. Det er felle #11 i bokas
feilregister, og på en programmeringsoppgave ville det gitt en feilmelding; på en
håndkjøring gir det et svar sensor ikke kan gi poeng for.

📜Wraparound-regelen
Probing-sekvensen for en nøkkel kk i en tabell med NN plasser er

h(k),(h(k)+1)modN,(h(k)+2)modN,h(k),\quad (h(k)+1) \bmod N,\quad (h(k)+2) \bmod N,\quad \ldots

Den besøker alle NN plassene før den kommer tilbake til utgangspunktet. Derfor:

- Sekvensen går aldri utenfor 00 til N1N-1. Er du på plass N1N-1 og skal
videre, er neste plass 0.
- Sekvensen finner alltid en ledig plass hvis det finnes én. Den prøver hele
tabellen.
- Den samme sekvensen må følges ved søk. Det er derfor Contains bruker
nøyaktig samme regnestykke som Insert.

Beslektet felle: #7 — å glemme siste ledd i en syklisk struktur, altså å
iterere rundt uten mod N. Den dukker opp igjen i NP-delen senere i boka, og den
har samme rot: en ring behandlet som en linje.

✏️Eksempel 2: Wraparound over slutten av tabellen

Sett inn nøklene 17, 26, 8, 35 og 9 i denne rekkefølgen i en tom hashtabell med
N=9N = 9 og h(k)=kmod9h(k) = k \bmod 9. Oppgi hele tabellen.

Hashverdiene: 17mod9=817 \bmod 9 = 8, 26mod9=826 \bmod 9 = 8, 8mod9=88 \bmod 9 = 8,
35mod9=835 \bmod 9 = 8, 9mod9=09 \bmod 9 = 0.

De fire første starter på plass 8 — den siste plassen i tabellen.

StegNøkkel kkh(k)h(k)Prøvde indekserTabell etter steget
11788_, _, _, _, _, _, _, _, 17
22688 -> 026, _, _, _, _, _, _, _, 17
3888 -> 0 -> 126, 8, _, _, _, _, _, _, 17
43588 -> 0 -> 1 -> 226, 8, 35, _, _, _, _, _, 17
5900 -> 1 -> 2 -> 326, 8, 35, 9, _, _, _, _, 17

Sluttilstand:
indeks:   0    1    2    3    4    5    6    7    8
T:        26   8    35   9    _    _    _    _    17
Eller på én linje: 26, 8, 35, 9, _, _, _, _, 17
Se på steg 2. Nøkkelen 26 hasher til plass 8, som er opptatt. Neste plass er
(8+1)mod9=0(8+1) \bmod 9 = 0 — altså helt i starten av tabellen. Uten mod N ville du
skrevet «plass 9», som ikke finnes.

Legg merke til hvordan klyngen ser ut. Den ligger på indeks 8, 0, 1, 2, 3 —

sammenhengende, men den «brekker» over kanten. Skriver du tabellen på papir, ser

den ut som to klynger; i virkeligheten er den én, fordi tabellen er en ring.

Kontrollregning. Fem nøkler, og antall prøver ble 1+2+3+4+4=141+2+3+4+4 = 14. Alle fem
plassene som ble fylt, ligger i probing-sekvensen fra 8, og ingen indeks utenfor
00 til 88 ble noen gang berørt. Den kontrollen — at ingen indeks er utenfor

området — tar to sekunder og fanger felle #11.
Fellenote. Fella her er felle #11 — probing som går utenfor 00 til N1N-1
fordi mod N er glemt.

📝Oppgave 2
Sjanger E

En hashtabell har N=8N = 8 og h(k)=kmod8h(k) = k \bmod 8. Sett inn nøklene
7, 15, 23, 6 og 31 i denne rekkefølgen.

a) Oppgi hele tabellen etter alle innsettingene.
b) Hvor mange plasser ble prøvd for nøkkelen 31?
c) Hvilken nøkkel havnet på en plass som ikke er dens egen hashverdi, uten at
den kolliderte med en nøkkel som hadde samme hashverdi?

Løkke 3 — lik nøkkel skal overskrives (ca. 12 min)

Den andre detaljen sensor trekker for, er kortere å forklare og like lett å
glemme.

En hashtabell lagrer hver nøkkel én gang. Setter du inn 10, og senere setter
inn 10 igjen, skal tabellen fortsatt inneholde nøyaktig én 10-er. Den nye verdien
overskriver den gamle på samme plass.

I pseudokoden er det den ene betingelsen and T[i] er ulik k som sørger for det.
Løkka stopper på to typer plasser: en tom plass, eller en plass som allerede
inneholder nøkkelen. I begge tilfeller er T[i] = k riktig handling.

Fjerner du den betingelsen, går løkka forbi den eksisterende 10-eren og fortsetter
til første tomme plass. Da står 10 to steder, og alle senere søk finner den
første — mens all data som ble lagret sammen med den andre, er utilgjengelig.
Dette er den andre halvdelen av felle #11.

Overskriving ved lik nøkkel

Møter probing-sekvensen en plass som allerede inneholder nøyaktig den nøkkelen
du skal sette inn, stopper den der og skriver over.

Konsekvensen: en hashtabell inneholder aldri to like nøkler, og antall prøver ved
en gjentatt innsetting er nøyaktig det samme som ved det første oppslaget. Uten
denne regelen får du duplikater som ingen senere søk vil finne.

✏️Eksempel 3: Samme nøkkel satt inn to ganger

Sett inn nøklene 10, 17, 24 og deretter 10 én gang til, i en tom hashtabell med
N=7N = 7 og h(k)=kmod7h(k) = k \bmod 7. Oppgi hele tabellen, og si hva som skiller riktig
og gal håndtering av det siste steget.

Hashverdiene: 10mod7=310 \bmod 7 = 3, 17mod7=317 \bmod 7 = 3, 24mod7=324 \bmod 7 = 3, og
10mod7=310 \bmod 7 = 3 igjen.

StegNøkkel kkh(k)h(k)Prøvde indekserTabell etter steget
11033_, _, _, 10, _, _, _
21733 -> 4_, _, _, 10, 17, _, _
32433 -> 4 -> 5_, _, _, 10, 17, 24, _
41033_, _, _, 10, 17, 24, _

Sluttilstand: _, _, _, 10, 17, 24, _
Se på steg 4. Probingen starter på plass 3, finner nøkkelen 10 der — og
stopper. Løkkebetingelsen er T[i] er ikke tom **and** T[i] er ulik k, og andre
ledd er usant, så løkka avsluttes umiddelbart. T[3] = 10 skriver over den samme
verdien, og tabellen er uendret. Bare én prøve.

Hva galt ville sett ut som. Uten and T[i] er ulik k ville løkka behandlet

plass 3 som opptatt og gått videre til 4, så 5, så 6 — og lagt inn en ny 10-er på
plass 6:
_, _, _, 10, 17, 24, 10
Tabellen har nå to 10-ere. Et søk etter 10 starter på plass 3, finner den første

og returnerer. Den andre kopien er permanent utilgjengelig, og plassen den tar er
tapt.
Fellenote. Dette er andre halvdel av felle #11 — probing som ikke

overskriver en lik nøkkel. Førstehalvdelen var manglende mod N. Begge sitter i

den samme while-linja, og begge koster poeng på sjanger I.

📝Oppgave 3
Sjanger E

En hashtabell har N=6N = 6 og h(k)=kmod6h(k) = k \bmod 6. Sett inn 13, 19, 13
og 25 i denne rekkefølgen.

a) Oppgi hele tabellen.
b) Hvor mange nøkler ligger i tabellen til slutt?
c) Hva ville tabellen sett ut som hvis Insert ikke hadde sjekket for lik
nøkkel?

Løkke 4 — søk, og de to kjøretidene (ca. 14 min)

— naturlig pausepunkt —

Å finne igjen en nøkkel bruker nøyaktig samme regnestykke som å sette den inn:
start på h(k)h(k), gå framover med mod N. Forskjellen er hva du gjør underveis og
når du stopper.

Du stopper når du finner nøkkelen — da finnes den — eller når du treffer en tom
plass
. Den tomme plassen er beviset på at nøkkelen ikke finnes: hadde den vært
satt inn, ville probing-sekvensen stoppet der, og den ville ligget på den plassen
eller tidligere.

📜Pseudokode-kontrakt: `Contains`
Antagelser om representasjon. Samme som for Insert: T er et array med N
plasser indeksert fra 0, hver plass er tom eller inneholder én nøkkel, og
h(k) = k mod N. Vi antar at ingen nøkler er slettet fra tabellen.

Prebetingelse: ingen. Postbetingelse: returverdien er sant hvis og bare
hvis k finnes i T; tabellen er uendret.

Procedure Contains(T, k)
  Input:  hashtabell T som array med N plasser (indeks fra 0), noekkel k
  Output: sant hvis k finnes i T, ellers usant
  N = T.length
  i = k mod N
  antall = 0
  while T[i] er ikke tom and antall < N:
      if T[i] er lik k:
          return sant
      i = (i + 1) mod N
      antall = antall + 1
  return usant

Invarianten i én setning: når løkka har prøvd plassene h(k)h(k) til ii, er k
garantert ikke å ligge på noen av dem — så finner du en tom plass, kan k ikke
finnes noe sted.

Hvorfor antall < N? For at søket skal stoppe i en full tabell. Uten
telleren ville probingen gå rundt i ring for alltid når ingen plass er tom og
nøkkelen ikke finnes. Dette er et kanttilfelle sensor gir delpoeng for å ha
tenkt på.

Kjøretid: O(1)O(1) forventet, O(n)O(n) i verste tilfelle. Verste tilfelle
inntreffer når nøklene ligger i én lang klynge, slik at sekvensen må gjennom hele.
Skriv alltid begge, og skriv alltid ordet «forventet».

✏️Eksempel 4: Søk i tabellen fra eksempel 1

Tabellen fra eksempel 1 er _, 15, 22, 8, 29, 3, _ med N=7N = 7. Søk etter
nøklene 29, 16 og 3, og vis probing-sekvensen for hver.

Nøkkelh(k)h(k)Prøvde indekserResultat
2911 -> 2 -> 3 -> 4funnet på plass 4
1622 -> 3 -> 4 -> 5 -> 6ikke funnet — plass 6 er tom
333 -> 4 -> 5funnet på plass 5

Se på søket etter 16. 16mod7=216 \bmod 7 = 2. Sekvensen går gjennom 22, 8, 29 og 3
uten treff, og stopper på plass 6, som er tom. Da vet vi sikkert at 16 ikke finnes
— for hadde den blitt satt inn, ville den ha stoppet på første ledige plass i sin
egen sekvens, og det ville vært plass 6 eller tidligere.

Fem prøver for å konkludere at noe ikke finnes. Det er den prisen
klyngedannelsen krever, og det illustrerer forskjellen mellom forventet og verste
tilfelle bedre enn noen tabell: i en tom tabell hadde det holdt med én prøve.
Merk søket etter 3. Nøkkelen ligger ikke på plass 3, som er dens egen

hashverdi, men på plass 5 — fordi klyngen sperret. Søket finner den likevel, fordi
det følger nøyaktig den samme sekvensen som innsettingen gjorde. Det er hele
grunnen til at probing-regelen må være deterministisk.

En viktig konsekvens du bør merke deg: dette forklarer hvorfor sletting er
vanskelig i lukket hashing. Fjerner du 8 fra plass 3 og lar plassen stå tom, vil
søket etter 29 stoppe på plass 3 og feilaktig melde «ikke funnet». Løsningen er å
markere plassen som slettet i stedet for tom — det er utenfor det som testes her,

men det er verdt å vite at problemet finnes.

📝Oppgave 4
Sjanger E og I

Tabellen 15, 23, 31, _, _, _, 6, 7 med N=8N = 8 er resultatet
fra oppgave 2.

a) Søk etter nøkkelen 23. Hvilke indekser prøves?
b) Søk etter nøkkelen 39. Hvilke indekser prøves, og hva blir svaret?
c) Hvorfor kan søket stoppe på en tom plass i stedet for å lete videre?

📝Oppgave 5
Sjanger I

Skriv Insert(T, k) for en hashtabell med lukket hashing og lineær
probing. Oppgi antagelser om representasjon og kjøretid, og forklar de to
detaljene som er lette å utelate.

📝Oppgave 6
Sjanger E, krevende

En hashtabell har N=11N = 11 og h(k)=kmod11h(k) = k \bmod 11. Sett
inn 31, 20, 42, 9, 53 og 64 i denne rekkefølgen.

a) Oppgi hele tabellen.
b) Hvor mange prøver kostet hver innsetting, og hva ble totalen?
c) Hva ville skjedd hvis rekkefølgen på nøklene hadde vært motsatt?

📝Oppgave 7
Sjanger I, krevende

En kollega har skrevet denne Insert-prosedyren:

Procedure Insert(T, k)
  Input:  hashtabell T med N plasser, noekkel k
  Output: T med k satt inn
  i = k mod T.length
  while T[i] er ikke tom:
      i = i + 1
  T[i] = k
  Kjoeretid: O(1)

a) Finn alle feilene.
b) Vis konkret hva som går galt med N=5N = 5, tabellen _, _, _, 8, 13 og
nøkkelen 18.
c) Skriv en korrekt versjon.

Begrepsbank

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

Probing-sekvensen

Rekken av plasser en nøkkel prøver: h(k)h(k), deretter (h(k)+1)modN(h(k)+1) \bmod N, og så
videre.

Den er deterministisk — nøyaktig samme rekke følges ved innsetting og ved søk.
Det er hele grunnen til at en nøkkel som ble forskjøvet under innsettingen,
likevel kan finnes igjen.

Wraparound

At probing-sekvensen går fra siste plass (N1N-1) tilbake til plass 0 i stedet for
utenfor arrayet. Det er mod N i uttrykket (i+1)modN(i+1) \bmod N som gjør det.

Uten mod N løper indeksen ut av tabellen — felle #11. Kontrollen tar to
sekunder: ingen indeks i sporingen din skal være større enn N1N-1.

Primær klyngedannelse

Fenomenet at nøkler som kolliderer, danner en sammenhengende blokk i tabellen —
og at blokken deretter fanger opp nøkler som hasher til hvilken som helst plass
inne i den.

Konsekvensen er at klynger vokser raskere jo større de er, og at en nøkkel kan bli
forskjøvet langt selv om ingen andre nøkler har samme hashverdi. Det er lineær
probings viktigste svakhet.

Hele tabellen som svarformat

Sensor ber om hele hashtabellen som sluttilstand, kommaseparert, med _ for
tomme plasser og indeks fra 0.

Å bare liste de innsatte nøklene er felle #11: de tomme plassene er en del av
svaret, fordi de viser at probing-sekvensen ble fulgt riktig.

Hashtabellens forventede kjøretid
O(1)O(1) for innsetting, oppslag og medlemskapstest — forventet, under
antagelsen om at hashfunksjonen sprer nøklene jevnt og at tabellen ikke er for
full.

Ordet «forventet» er ikke pynt. Uten det er påstanden gal, og den er en av de
hyppigste sant/usant-fellene i faget.

Hashtabellens verste kjøretid
O(n)O(n), der nn er antall nøkler i tabellen. Det inntreffer når alle nøklene har
havnet i én sammenhengende klynge, slik at probing-sekvensen må gjennom hele.

En hashtabell der alt kolliderer, er ikke bedre enn et lineært søk gjennom en
usortert liste. Det er derfor rehashing finnes — se
kap. 3.2.

Tom plass som stoppkriterium i søk
Contains stopper og svarer «ikke funnet» når den treffer en tom plass.

Argumentet: hadde nøkkelen vært satt inn, ville Insert fulgt samme sekvens og
stoppet på den første ledige plassen, altså denne eller en tidligere. Argumentet
forutsetter at ingenting er slettet — det er derfor lukket hashing markerer
slettede plasser i stedet for å tømme dem.

Tabellstørrelsen N

Antall plasser i arrayet, indeksert 00 til N1N-1. Ikke det samme som nn, som er
antall nøkler som faktisk er lagret.

Hashfunksjonen h(k)=kmodNh(k) = k \bmod N avhenger av NN, og derfor endrer alle
hashverdier seg når tabellen utvides — se rehashing i
kap. 3.2.

Lukket kontra åpen hashing
Lukket hashing (også kalt åpen adressering) lagrer alle nøkler i selve
arrayet og løser kollisjoner ved probing. Åpen hashing (kjeding) lar hver
plass peke til en lenket liste med alle nøkler som hasher dit.

IN2010 håndkjører lukket hashing med lineær probing. Kjeding nevnes som
alternativ, men det er ikke den varianten som testes.

Modulo som operator
kmodNk \bmod N er resten når kk deles på NN. Resultatet ligger alltid mellom 00 og
N1N-1 for positive kk.

I bokas pseudokode skrives operatoren mod, ikke %. Regneeksempel:
22mod7=122 \bmod 7 = 1, fordi 22=37+122 = 3 \cdot 7 + 1. Regn ut alle hashverdiene før du
begynner å sette inn — det er den enkleste måten å unngå regnefeil under
tidspress.

Sjanger E — håndkjøring av datastruktur

Del 1-sjangeren der du får en operasjonsrekke og skal oppgi kun sluttilstanden
til strukturen.

Minst én per sett, ofte to, og linear probing er den nest hyppigste etter heap.
Formatet er strengt: hele tabellen, _ for tomme plasser, indeks fra 0. Delvis
riktig tilstand gir delvis uttelling, så la aldri svaret stå tomt.

Sjanger I — hashing og søk i pseudokode

Del 2-sjangeren der du skriver Insert, Contains, et modifisert binærsøk eller
en duplikat-/parsøkalgoritme, og oppgir kjøretiden.

Har vært på så godt som alle sett. Full uttelling krever fire ledd: problemet
navngitt, antagelser om representasjon, algoritmen, og kjøretid som matcher koden
med nn definert.

Hvorfor sletting er vanskelig i lukket hashing

Tømmer du en plass midt i en klynge, vil senere søk stoppe der og feilaktig melde
«ikke funnet» for nøkler som ligger lenger ute i sekvensen.

Løsningen er å markere plassen som slettet — en tilstand som ikke stopper søk,
men som kan gjenbrukes ved innsetting. Detaljen håndkjøres ikke på eksamen, men
den forklarer hvorfor Contains-argumentet forutsetter at ingenting er slettet.

Antall prøver som mål på klyngedannelse

Summen av prøver over alle innsettingene sier hvor hardt klyngedannelsen slår
til. På en tom tabell er den 1 per innsetting; i en full klynge vokser den
lineært.

Bruk den som kontrollregning i en håndkjøring: får du 21 prøver på seks nøkler,
har du hatt maksimal kollisjon, og det bør stemme med at alle hadde samme
hashverdi.

Hashing er tyngre i IN2010 enn i et generisk pensum

Her testes hashing både som håndkjøring (sjanger E) og som pseudokode
(sjanger I), i hvert eneste sett — 7 av 7 (100 %).

Til sammenligning er hashing i NTNUs TDT4120 et faktapunkt i under halvparten av
settene. Kommer du fra et slikt pensum, eller fra en generisk algoritmebok, er
dette temaet du systematisk undervurderer.

Repetisjon — kapitlet på ett kort

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 Universitetet i Oslo. Dette er ikke offisielt studiemateriell. Les mer.