Tilbake
6.1

6.1 Topologisk sortering og DAG-egenskaper

Topologisk sortering med Kahns algoritme (inngrad-0-kø) og sykeldeteksjon i rettet graf — det foretrukne verktøyet for avhengighetsproblemer.

45 min
7 oppgaver
Topologisk sorteringDAG-egenskaper
Din fremgang i kapitlet
0 / 7 oppgaver

Forkunnskaper

- kap. 5.1 — grafnotasjonen G=(V,E)G = (V, E), nabolister og
begrepet inngrad (hvor mange kanter som peker inn til en node).
- kap. 5.2 — BFS og DFS. Kahns algoritme har samme form som
en BFS: en kø, ett element av gangen.
- kap. 1.2 — løkketelling. Kjøretiden O(V+E)O(|V| + |E|) leses
ut av at hver node og hver kant berøres én gang.

Har du ikke jobbet med nøstede strukturer i kode før, er
Nøstede lister og ordbøker et mykt sted å begynne — en
naboliste er akkurat det: en liste av lister.

Notasjons- og pseudokodeliste

Løkke 1 — huset som må bygges i riktig rekkefølge (ca. 12 min)

Et modulbygg settes opp av åtte arbeidslag. Grunnmuren må stå før gulvmodulene
kan legges, gulvmodulene før heissjakten kan reises, og fasadekledningen kan først
komme når både taket og heissjakten er ferdige. Ingen av lagene bryr seg om noe
annet enn sine egne forutsetninger.

Spørsmålet byggelederen sitter med, er ikke vanskelig å formulere: finnes det en
rekkefølge der hvert lag kan gjøre jobben sin uten å vente på noe som ikke er
gjort?
Og hvis den finnes — hvordan finner vi den, uten å prøve oss fram?

Dette er et grafproblem. Hvert arbeidslag er en node, og en pil fra uu til vv
betyr «uu må være ferdig før vv kan starte». Da er spørsmålet: kan nodene stilles
opp på en linje slik at alle pilene peker framover?

DAG (rettet asyklisk graf)

En rettet graf uten sykler: du kan aldri følge pilene fra en node og komme
tilbake til den samme noden igjen.

Forkortelsen kommer fra engelsk «directed acyclic graph». Ordet asyklisk betyr
nettopp «uten sykel». Enhver avhengighetsstruktur som faktisk lar seg gjennomføre,
er en DAG — og motsatt: er den ikke en DAG, finnes det en gruppe oppgaver som alle
venter på hverandre.

Topologisk rekkefølge

En rekkefølge på nodene i en rettet graf der hver kant peker framover: går det
en kant fra uu til vv, kommer uu før vv i rekkefølgen.

Legg merke til at rekkefølgen sjelden er entydig. Har to noder ingen
avhengighet mellom seg, kan de stå i hvilken som helst innbyrdes rekkefølge, og
begge svarene er like riktige. Sensor godtar enhver lovlig rekkefølge.

Inngrad

Antall kanter som peker inn til en node. Utgraden er tilsvarende antall kanter
som peker ut.

Inngraden er hele nøkkelen til topologisk sortering: en node med inngrad 0 har
ingenting foran seg og kan gjøres med det samme. Summen av alle inngrader er
E|E| — hver kant teller én gang.

📜Topologisk rekkefølge finnes hvis og bare hvis grafen er en DAG

En rettet graf har en topologisk rekkefølge nøyaktig når den ikke har noen
sykel.

Den ene veien er lett å se: har grafen en sykel v1v2v1v_1 \to v_2 \to \ldots \to v_1,
måtte v1v_1 kommet før v2v_2, som måtte kommet før v1v_1. Det er umulig.

Den andre veien er selve algoritmen: Kahns metode konstruerer en rekkefølge så
lenge grafen er asyklisk, og stopper med noder til overs hvis den ikke er det. Du
trenger ikke bevise dette på eksamen, men du skal kunne bruke begge retningene:
«ingen lovlig rekkefølge» og «grafen har en sykel» er det samme utsagnet.

✏️Eksempel 1: Kahns algoritme på modulbygget

Arbeidslagene er AA = grunnmur, BB = gulvmoduler, CC = bæresøyler,
DD = veggmoduler, EE = trapperom, FF = tak, GG = heissjakt og
HH = fasadekledning. Avhengighetene er

A -> B, A -> C, B -> D, C -> D, C -> E, D -> F, E -> F, B -> G,
G -> H, F -> H.

Finn en lovlig rekkefølge, eller vis at ingen finnes.

Steg 0 — inngradstabellen. Tell hvor mange piler som peker inn til hver node:

A=0, B=1, C=1, D=2, E=1, F=2, G=1, H=2

Kontrollregning: summen er 0+1+1+2+1+2+1+2=100+1+1+2+1+2+1+2 = 10, og grafen har 10 kanter. Stemmer.

Bare AA har inngrad 0, så køen starter som A.

Sporingen. Køen holdes i alfabetisk rekkefølge her, slik at sporingen er
mulig å gjenskape nøyaktig. Det er ikke et krav i algoritmen — enhver rekkefølge i
køen gir en lovlig topologisk rekkefølge.

StegPlukketNaboers inngrad senkesInngrader etterKø etterRekkefølge så langt
1AB: 1 -> 0, C: 1 -> 0A=0, B=0, C=0, D=2, E=1, F=2, G=1, H=2B, CA
2BD: 2 -> 1, G: 1 -> 0A=0, B=0, C=0, D=1, E=1, F=2, G=0, H=2C, GA, B
3CD: 1 -> 0, E: 1 -> 0A=0, B=0, C=0, D=0, E=0, F=2, G=0, H=2D, E, GA, B, C
4DF: 2 -> 1A=0, B=0, C=0, D=0, E=0, F=1, G=0, H=2E, GA, B, C, D
5EF: 1 -> 0A=0, B=0, C=0, D=0, E=0, F=0, G=0, H=2F, GA, B, C, D, E
6FH: 2 -> 1A=0, B=0, C=0, D=0, E=0, F=0, G=0, H=1GA, B, C, D, E, F
7GH: 1 -> 0A=0, B=0, C=0, D=0, E=0, F=0, G=0, H=0HA, B, C, D, E, F, G
8HingenA=0, B=0, C=0, D=0, E=0, F=0, G=0, H=0tomA, B, C, D, E, F, G, H

Sluttilstand — dette er svaret du leverer:
A, B, C, D, E, F, G, H
Alle 8 nodene ble prosessert, så grafen er en DAG, og rekkefølgen er lovlig.
Kontrollen som tar ti sekunder: gå gjennom hver kant og sjekk at venstre node
står før høyre i svaret ditt. A -> B: ja. B -> G: ja. F -> H: ja. Alle ti
kanter peker framover.
Fellenote. Fella her er å tro at rekkefølgen er entydig. Prøv å plukke CC før
BB i steg 2 — begge har inngrad 0 der — og du får A, C, B, E, D, F, G, H, som er
like riktig. Sensor sjekker at kantene peker framover, ikke at du traff «fasiten».
📝Oppgave 1

(Innstegsoppgave, sjanger H — grafalgoritme, altså at du gjenkjenner problemet og
bruker riktig algoritme.) En rettet graf har nodene P,Q,R,SP, Q, R, S og kantene
P -> R, Q -> R, R -> S.

a) Skriv opp inngraden til hver node.
b) Hvilke noder kan Kahns algoritme starte med?
c) Skriv opp to forskjellige lovlige topologiske rekkefølger.

Løkke 2 — algoritmen skrevet ned (ca. 15 min)

Sporingen i eksempel 1 er hele algoritmen. Nå skal den skrives ned slik du må
kunne den på eksamen: som en Procedure med input, output og oppgitt kjøretid.

Metoden heter Kahns algoritme etter Arthur Kahn, som beskrev den i 1962. Ideen
er den samme som byggelederens: gjør alt som kan gjøres nå, og se hva som blir
klart av det.

📜Pseudokode-kontrakt: `TopologiskSortering`
Antagelser om representasjon. Grafen G=(V,E)G = (V, E) er gitt som nabolister:
G.naboer(v) gir alle noder w med en kant v -> w, og en gjennomgang av alle
nabolister koster O(V+E)O(|V| + |E|) til sammen. inngrad er et array indeksert på
node. Køen er en vanlig kø med O(1)O(1) innsetting og uttak.

Prebetingelse: ingen — algoritmen virker på enhver rettet graf.
Postbetingelse: returverdien er enten en lovlig topologisk rekkefølge med alle
V|V| nodene, eller meldingen «syklisk».

Procedure TopologiskSortering(G)
  Input:  rettet graf G = (V, E) som nabolister
  Output: en liste med alle nodene i topologisk rekkefolge,
          eller meldingen «syklisk» hvis ingen slik rekkefolge finnes
  for hver v i V:
      inngrad[v] = 0
  for hver v i V:
      for hver w i G.naboer(v):
          inngrad[w] = inngrad[w] + 1
  Ko = tom ko
  for hver v i V:
      if inngrad[v] er 0:
          Ko.leggTil(v)
  rekkefolge = tom liste
  while Ko er ikke tom:
      v = Ko.taUt()
      rekkefolge.leggTilBakerst(v)
      for hver w i G.naboer(v):
          inngrad[w] = inngrad[w] - 1
          if inngrad[w] er 0:
              Ko.leggTil(w)
  if lengden av rekkefolge er ulik |V|:
      return «syklisk»
  return rekkefolge

Grunnideen i én setning: en node kan trygt plasseres i rekkefølgen så snart
alt som må komme før den, allerede er plassert — og det ser du på at inngraden er
falt til null.

Kjøretid: O(V+E)O(|V| + |E|). Tell løkkene: den første går over alle noder,
O(V)O(|V|). Den andre går over alle nabolister, altså hver kant én gang,
O(E)O(|E|). Den tredje går over alle noder igjen, O(V)O(|V|). while-løkka tar hver
node ut av køen nøyaktig én gang (en node legges i køen bare når inngraden når
null, og den når null bare én gang), og for hver av dem gås nabolisten gjennom én
gang — til sammen O(V)O(|V|) uttak og O(E)O(|E|) kantbehandlinger. Summen er
O(V+E)O(|V| + |E|), ikke et produkt: løkkene står etter hverandre, ikke inni
hverandre.

Legg merke til hvorfor køen er der. Uten den måtte du lete gjennom hele
inngradstabellen etter en null hver eneste runde — V|V| runder à O(V)O(|V|) leting,
altså O(V2)O(|V|^2). Køen husker hvem som ble klar, og det er hele forskjellen
mellom en lineær og en kvadratisk løsning.

Merk også at det står , ikke stack. Begge virker. En stack gir en annen
lovlig rekkefølge, ikke en gal en. Skriver du stack i besvarelsen din, er det
riktig så lenge du er konsekvent.

✏️Eksempel 2: Hvorfor $O(|V| + |E|)$ og ikke $O(|V| \cdot |E|)$

En medstudent hevder at Kahns algoritme er O(VE)O(|V| \cdot |E|), «fordi
while-løkka går over alle nodene, og inni den går vi gjennom kantene». Vis at
det er feil, ved å telle nøyaktig hvor mange ganger hver linje utføres på
modulbygget fra eksempel 1.

Feilen ligger i ordet «kantene». Den indre løkka går ikke gjennom alle
kantene for hver node — den går gjennom nabolista til nettopp den noden som
ble tatt ut.

Tellingen for modulbygget (V=8|V| = 8, E=10|E| = 10):

Node tatt utAntall naboerKantbehandlinger
A2 (B, C)2
B2 (D, G)2
C2 (D, E)2
D1 (F)1
E1 (F)1
F1 (H)1
G1 (H)1
H00
Sum10

Summen er 10 — nøyaktig E|E|. Det er ikke tilfeldig: hver kant v -> w ligger i
nøyaktig én naboliste (nemlig vv sin), og den lista gås gjennom nøyaktig én gang,
den gangen vv tas ut av køen.
Konklusjonen: while-løkka koster O(V)O(|V|) uttak pluss O(E)O(|E|)
kantbehandlinger, ikke O(V)O(|V|) ganger O(E)O(|E|). Kjøretiden er O(V+E)O(|V| + |E|).
Dette er et generelt mønster som går igjen i hele grafdelen av faget: når en
løkke over noder inneholder en løkke over den nodens naboer, blir summen
O(V+E)O(|V| + |E|) — ikke et produkt. Samme argument gir kjøretiden til BFS, DFS og
komponentsøk i kap. 5.2.
Poengtrapp-notat. På eksamen er det å oppgi O(VE)O(|V| \cdot |E|) her et rent

tap: kjøretiden matcher ikke algoritmen du faktisk ga, og det er en av de tre
tingene sensorveiledningene nevner eksplisitt at det trekkes for.

📝Oppgave 2
Sjanger H

En rettet graf har nodene AA til FF og kantene
A -> C, B -> C, C -> D, C -> E, D -> F, E -> F.

a) Skriv opp inngradstabellen, og kontrollér den mot antall kanter.
b) Håndkjør Kahns algoritme og oppgi rekkefølgen. Bruk alfabetisk rekkefølge i
køen.
c) Hvor mange forskjellige lovlige topologiske rekkefølger har denne grafen?

Løkke 3 — når det ikke finnes noen rekkefølge (ca. 12 min)

Halvparten av eksamensoppgavene på dette temaet spør ikke bare om rekkefølgen. De
spør: finnes det en i det hele tatt? Formuleringen er gjerne «meld fra hvis en
sirkulær avhengighet gjør det umulig».

Det gode med Kahns algoritme er at du får svaret gratis. Du trenger ikke en egen
sykeldeteksjon — du trenger bare å telle.

📜Sykeldeteksjon: tell hvor mange noder som ble prosessert

Kjør Kahns algoritme til køen er tom. Hvis rekkefølgen inneholder færre enn
V|V| noder
, har grafen en sykel, og ingen topologisk rekkefølge finnes.

Hvorfor det stemmer: en node blir liggende igjen bare hvis inngraden aldri når
null, altså hvis den har en forgjenger som selv aldri ble tatt ut. Følger du den
kjeden bakover i en endelig graf, må du før eller siden komme tilbake til en node
du har vært innom — og det er en sykel.

Bonusen: nodene som ble liggende igjen, er nøyaktig de nodene som ligger på
eller nedstrøms for en sykel. Du kan altså peke på hvor problemet er, ikke bare si
at det finnes. Det er verdt et delpoeng når oppgaven ber om at algoritmen skal
«melde fra».

✏️Eksempel 3: En syklisk avhengighet i et rørsystem

Et anlegg pumper væske gjennom seks trinn. Trinn AA mater BB og EE, BB mater
CC, CC mater DD, DD mater tilbake til BB (en returledning som ble koblet
feil), og EE mater FF.

Kantene er A -> B, B -> C, C -> D, D -> B, A -> E, E -> F.

Kan trinnene settes i drift i en rekkefølge der hvert trinn har væske når det
starter? Håndkjør Kahns algoritme.

Inngradstabellen: A=0, B=2, C=1, D=1, E=1, F=1. Summen er 6, og grafen har 6
kanter. Bare AA har inngrad 0.

StegPlukketNaboers inngrad senkesInngrader etterKø etterRekkefølge så langt
1AB: 2 -> 1, E: 1 -> 0A=0, B=1, C=1, D=1, E=0, F=1EA
2EF: 1 -> 0A=0, B=1, C=1, D=1, E=0, F=0FA, E
3FingenA=0, B=1, C=1, D=1, E=0, F=0tomA, E, F

Sluttilstand — dette er svaret du leverer:
syklisk — ingen topologisk rekkefolge finnes
Tellingen som avgjør: algoritmen prosesserte 3 noder (AA, EE, FF), men
grafen har 6. Siden 363 \neq 6, er grafen ikke en DAG.

Hvor sykelen er. Nodene som ble liggende igjen, er BB, CC og DD — med

inngradene B=1, C=1, D=1 når køen gikk tom. Ingen av dem nådde null, fordi

BB venter på DD, DD venter på CC, og CC venter på BB. Det er sykelen
B -> C -> D -> B, altså returledningen.

Merk at EE og FF ble prosessert helt normalt. En sykel ett sted i grafen
ødelegger ikke for resten — men den gjør at hele grafen mangler en topologisk
rekkefølge, fordi rekkefølgen må inneholde alle nodene.
Fellenote. Den vanligste feilen her er å levere A, E, F som svar. Det er

ikke en topologisk rekkefølge av grafen — det er tre noder av seks. Algoritmen
skal melde fra, og en besvarelse som ikke sjekker antallet, mister som regel
et helt delpoeng selv om pseudokoden ellers er riktig.

📝Oppgave 3
Sjanger H

Fem oppgaver i et prosjekt har avhengighetene
A -> B, B -> C, C -> E, E -> B, A -> D, D -> E.

a) Håndkjør Kahns algoritme og oppgi hva den svarer.
b) Hvilke noder ble liggende igjen, og hva forteller det deg?
c) Én kant må fjernes for at rekkefølgen skal bli mulig. Hvilken, og hva blir
rekkefølgen da?

Løkke 4 — de to alternativene, og hva sensor egentlig ser etter (ca. 10 min)

Kahn er ikke den eneste veien. To alternativer nevnes i pensum, og begge kan gi
full uttelling hvis de er presist beskrevet.

DFS med tre tilstander. Gi hver node en av tilstandene uoppdaget, under
prosessering
og ferdig. Gjør en dybde-først-traversering; møter du en node som
er under prosessering, har du funnet en sykel. Er alle ferdige, gir nodene i
omvendt rekkefølge av når de ble ferdige, en topologisk rekkefølge. Kjøretiden
er den samme, O(V+E)O(|V| + |E|).

Grunnen til at de tre tilstandene er nødvendige: en node som er ferdig, kan du
godt møte på nytt — det betyr bare at to stier fører til samme sted. En node som
er under prosessering, ligger derimot på stien du står på akkurat nå, og da har
du gått i ring.

Sterkt sammenhengende komponenter. En rettet graf har en sykel hvis og bare
hvis minst én sterkt sammenhengende komponent inneholder mer enn én node. Det
gir en sykel-test i O(V+E)O(|V| + |E|), og du får i tillegg vite hvilke noder som
henger sammen. Se kap. 5.4.

Kan dette gjøres raskere? Nei. Du må se på hver kant minst én gang for å vite
at den peker riktig vei, så O(V+E)O(|V| + |E|) er nedre grense for problemet. Alle tre
metodene treffer den. Når tre metoder har samme kjøretid, velger du den du kan
skrive presist — og det er poenget sensorveiledningene gjentar: en klar forklaring
er verdt mer enn en uklar pseudokode.

✏️Eksempel 4: Eksamensnivå — installasjonsrekkefølge med feilmelding

Et system skal installere nn programpakker. Hver pakke kan kreve at andre pakker
er installert først. Skriv en algoritme som enten gir en installasjonsrekkefølge,
eller melder at kravene er umulige å oppfylle. Oppgi antagelser og kjøretid, og si
hvorfor kjøretiden ikke kan bli lavere.

Problemet navngitt. Dette er en avhengighetsstruktur der noe må komme før noe
annet, og spørsmålet er om det finnes en lovlig rekkefølge. Det er topologisk
sortering
, og sykeltesten er innebygd.

Antagelser om representasjon. Pakkene er nodene i en rettet graf
G=(V,E)G = (V, E) med V=n|V| = n pakker; en kant p -> q betyr at p må installeres før
q. Grafen er gitt som nabolister, slik at G.naboer(p) gir alle pakker som
venter på p. Antall avhengigheter er E|E|.

Algoritmen.

Procedure Installasjonsrekkefolge(G)
  Input:  rettet graf G = (V, E) over pakker; kant p -> q betyr «p foer q»
  Output: en installasjonsrekkefolge, eller meldingen «sirkulaer avhengighet»
  for hver v i V:
      inngrad[v] = 0
  for hver v i V:
      for hver w i G.naboer(v):
          inngrad[w] = inngrad[w] + 1
  Ko = tom ko
  for hver v i V:
      if inngrad[v] er 0:
          Ko.leggTil(v)
  rekkefolge = tom liste
  while Ko er ikke tom:
      v = Ko.taUt()
      rekkefolge.leggTilBakerst(v)
      for hver w i G.naboer(v):
          inngrad[w] = inngrad[w] - 1
          if inngrad[w] er 0:
              Ko.leggTil(w)
  if lengden av rekkefolge er ulik |V|:
      return «sirkulaer avhengighet», og de gjenvaerende nodene
  return rekkefolge

Kjøretid: O(V+E)O(|V| + |E|), der V=n|V| = n er antall pakker og E|E| er antall
avhengigheter. Hver node tas ut av køen én gang, og hver kant behandles én gang.

Hvorfor dette er lavest mulig. Enhver korrekt algoritme må lese hver
avhengighet minst én gang — en avhengighet den ikke har sett, kan være den som
gjør rekkefølgen umulig. Det gir Ω(V+E)\Omega(|V| + |E|) som nedre grense, og Kahn
treffer den. Det er derfor dette svaret ligger øverst i poengtrappen: en løsning
som prøver seg fram med permutasjoner, eller som leter gjennom inngradstabellen
hver runde (O(V2)O(|V|^2)), er korrekt, men tregere — og «lavere kjøretid er mer
poenggivende» er den mest gjentatte poengregelen i faget.

Det siste delpoenget ligger i returlinja: at algoritmen ikke bare svarer
«umulig», men også peker på hvilke pakker som inngår i den sirkulære
avhengigheten. Det er gratis — det er nettopp nodene som ble liggende igjen.

📝Oppgave 4
Sjanger F

Sett
kryss for hvert utsagn: sant eller usant?

a) Enhver rettet graf har minst én topologisk rekkefølge.
b) En DAG kan ha flere forskjellige topologiske rekkefølger.
c) Kahns algoritme er O(V+E)O(|V| + |E|) når køen er en vanlig kø.
d) Hvis Kahns algoritme prosesserer alle nodene, er grafen en DAG.
e) En urettet graf kan sorteres topologisk.

📝Oppgave 5
Sjanger H

Skriv ErDAG(G) — en prosedyre som returnerer sant hvis den
rettede grafen G er asyklisk, og usant ellers. Oppgi antagelser om
representasjon og kjøretid.

Forklar til slutt med én setning hvorfor du ikke trenger å lete etter selve
sykelen for å svare.

📝Oppgave 6
Sjanger H, krevende

En DAG kan ha mange lovlige topologiske rekkefølger. Skriv
en algoritme som avgjør om rekkefølgen er entydig, altså om det finnes
nøyaktig én.

a) Formulér betingelsen for entydighet i én setning.
b) Skriv algoritmen i pseudokode, og oppgi kjøretiden.
c) Sjekk betingelsen på grafen med kantene A -> B, B -> C, C -> D,
A -> C, B -> D.

📝Oppgave 7
Sjanger H, krevende

I et byggeprosjekt med V|V| oppgaver og E|E|
avhengigheter vil prosjektlederen vite, for én bestemt oppgave tt, hvor mange
andre oppgaver som må være ferdige før tt kan starte — direkte eller indirekte.

a) En kollega foreslår: «kjør en søking fra hver node og se om den når tt».
Hva blir kjøretiden?
b) Gi en løsning med lavere kjøretid, og oppgi den.
c) Hvor mange er svaret for oppgave FF i modulbygget fra eksempel 1?

Begrepsbank

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

Kahns algoritme

Topologisk sortering ved hjelp av inngrader: legg alle noder med inngrad 0 i en
kø, ta ut én av gangen, legg den i rekkefølgen, og senk inngraden til alle naboene
dens. Når en nabos inngrad når 0, legges den i køen.

Kjøretid O(V+E)O(|V| + |E|). Melder «syklisk» hvis færre enn V|V| noder ble
prosessert.

Sykel i rettet graf

En sti som følger pilene og ender der den startet. En rettet graf med sykel har
ingen topologisk rekkefølge.

Test i O(V+E)O(|V| + |E|): kjør Kahn og sammenlign antall prosesserte noder med V|V|.
Alternativt: DFS med tre tilstander, eller en SCC-kjøring der en komponent med mer
enn én node avslører sykelen.

DFS med tre tilstander

Sykeldeteksjon ved dybde-først-søk der hver node er uoppdaget, under
prosessering
eller ferdig. Møter søket en node som er under prosessering,
finnes en sykel.

Kjøretid O(V+E)O(|V| + |E|). To tilstander er ikke nok: en ferdig node kan nås fra
to ulike stier uten at det finnes noen sykel.

Kildenode og sluttnode

En kildenode har inngrad 0 — ingenting peker inn til den. En sluttnode har
utgrad 0 — den peker ikke videre.

Enhver ikke-tom DAG har minst én av hver. Kahns algoritme starter alltid i
kildenodene, og de siste nodene i rekkefølgen er alltid sluttnoder.

Hvorfor køen gjør Kahn lineær

Uten kø må du lete gjennom inngradstabellen etter en node med inngrad 0 hver
runde: V|V| runder à O(V)O(|V|) leting gir O(V2)O(|V|^2).

Køen husker hvilke noder som nettopp ble klare, så hver node legges inn og tas ut
nøyaktig én gang. Det er forskjellen mellom O(V2)O(|V|^2) og O(V+E)O(|V| + |E|) — og
mellom nederste og øverste trinn i poengtrappen.

Kø eller stack i Kahns algoritme

Begge virker, og begge gir en lovlig topologisk rekkefølge — de gir bare
forskjellige rekkefølger.

Køen gir en «bredde-først»-aktig rekkefølge, stacken en «dybde-først»-aktig. Sensor
godtar enhver lovlig rekkefølge, så velg den du klarer å skrive konsekvent.

Entydig topologisk rekkefølge

Rekkefølgen er entydig nøyaktig når køen aldri inneholder mer enn én node
samtidig.

Ligger to noder i køen på samme tid, har de ingen binding til hverandre og kan
bytte plass, og da finnes minst to lovlige rekkefølger. Testen koster ingenting
ekstra: O(V+E)O(|V| + |E|).

Reversert graf

Grafen GRG^R der hver kant u -> v er snudd til v -> u. Å bygge den koster
O(V+E)O(|V| + |E|) — én gjennomgang av alle nabolistene.

Den er verktøyet når spørsmålet er snudd: «hvem kan nå tt» i GG er det samme som
«hvem kan nås fra tt» i GRG^R, og det svares med én traversering i stedet for
V|V|. Trikset kommer igjen i kap. 6.2 med Dijkstra.

Kjøretiden O(V+E)O(|V| + |E|) — hva den betyr

Lineær i grafens størrelse, ikke i antall noder alene. En graf med mange
kanter koster mer enn en med få, selv om nodeantallet er likt.

Mønsteret dukker opp hver gang en løkke over nodene inneholder en løkke over
den nodens naboer: summen av alle nabolistene er E|E|, ikke VE|V| \cdot |E|.
Samme argument gir kjøretiden til BFS, DFS, topologisk sortering og
komponentsøk.

Vektet DAG og korteste vei

I en vektet DAG finner du korteste vei ved først å sortere topologisk, og så
gå gjennom nodene i den rekkefølgen og oppdatere avstandene.

Kjøretiden er O(V+E)O(|V| + |E|) — raskere enn Dijkstra, og metoden takler til og med
negative kantvekter. Dette er én av de fire radene i korteste-vei-matrisen i
kap. 6.2.

Mønstergjenkjenning: når er svaret topologisk sortering?

Når oppgaveteksten bruker ord som avhengighet, «må gjøres før»,
«installasjonsrekkefølge», «forutsetning» eller «sirkulær».

Da skal du gjenkjenne, ikke oppfinne: skriv Kahn, oppgi O(V+E)O(|V| + |E|), og legg
til sykelsjekken. Mønsteret er dokumentert i 5 av 7 sett i arkivet.

Summen av inngrader
vVinngrad(v)=E\sum_{v \in V} \text{inngrad}(v) = |E|

Hver kant bidrar med nøyaktig 1 til inngraden til noden den peker på. Bruk det som
kontrollregning når du har fylt ut inngradstabellen for hånd — stemmer ikke summen
med antall kanter, har du telt feil, og hele håndkjøringen blir gal.

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.