edubun
Nederlands

Grafen doorzoeken: BFS en DFS

In 1736 toonde Leonhard Euler aan dat geen wandeling elk van de zeven bruggen van Koningsbergen precies eenmaal oversteekt. Met dat antwoord begon de grafentheorie.

Punten en lijnen

Een graaf is een verzameling punten, knopen genoemd, verbonden door lijnen, kanten genoemd. Het idee is eenvoudig en past bij enorm veel dingen: steden verbonden door wegen, webpagina's door links, mensen door vriendschappen en doolhofvakjes verbonden waar geen muur is. Zodra je een probleem als graaf tekent, kunnen dezelfde paar algoritmen vragen beantwoorden zoals ‘kan ik van hier naar daar?’ en ‘wat is de kortste weg?’

AchtergrondDe stad Koningsbergen (nu Kaliningrad) had zeven bruggen tussen twee eilanden en de twee rivieroevers. Mensen vroegen of een wandeling elke brug precies eenmaal kon oversteken. In 1736 antwoordde Leonhard Euler nee. Zijn belangrijkste stap was de kaart weggooien: alleen de landgebieden en bruggen ertussen telden. Elk landgebied werd een punt en elke brug een lijn. Hij toonde daarna aan dat zo'n wandeling bij elk punt, behalve mogelijk begin en einde, een even aantal lijnen vereist. In Koningsbergen hadden alle vier landgebieden een oneven aantal.
A B C D E F
De voorbeeldgraaf in dit hoofdstuk. A is de start; F het doel. Kanten: A‑B, A‑C, B‑D, C‑D, C‑E, D‑F, E‑F.

Een graaf opslaan in code

Een gebruikelijke manier om een graaf op te slaan is een burenlijst: voor elke knoop een lijst van zijn buren. De notities bij MIT 6.006 gebruiken precies dit: een array of woordenboek Adj waarin Adj[u] de met u verbonden knopen opsomt. Voor de graaf hierboven ziet dat er zo uit:

const graph = {
  A: ["B", "C"],
  B: ["A", "D"],
  C: ["A", "D", "E"],
  D: ["B", "C", "F"],
  E: ["C", "F"],
  F: ["D", "E"],
};

Breedte-eerst zoeken: ring voor ring

Breedte-eerst zoeken (BFS) verkent een graaf in ringen rond de start. Eerst de start zelf, op afstand 0. Dan alle buren, op afstand 1. Dan elke nieuwe knoop naast die buren, op afstand 2, enzovoort. Hiervoor houdt het een wachtrij bij, waarin wie eerst binnenkomt er eerst uitgaat. Neem de voorste knoop, bezoek die en voeg nog ongeziene buren achteraan toe. Vanaf A bezoekt BFS in het voorbeeld A, dan B en C, dan D en E en dan F. Omdat het ring voor ring naar buiten gaat, vindt BFS bij de eerste keer bereiken van een knoop een pad met de minste kanten. F ligt 3 kanten van A, bijvoorbeeld A, B, D, F.

function bfs(graph, start) {
  const seen = new Set([start]);
  const queue = [start];
  const order = [];
  while (queue.length) {
    const u = queue.shift();        // first in, first out
    order.push(u);
    for (const v of graph[u]) {
      if (!seen.has(v)) { seen.add(v); queue.push(v); }
    }
  }
  return order;   // A B C D E F
}

Diepte-eerst zoeken: zo ver mogelijk, dan terug

Diepte-eerst zoeken (DFS) gedraagt zich als iemand die in een doolhof één hand aan de muur houdt. Het volgt één pad zo diep mogelijk. Bij een doodlopend einde of al bezochte plek gaat het terug naar het recentste keuzepunt en probeert de volgende optie. Het gebruikt een stapel in plaats van een wachtrij, laatst erin, eerst eruit, en is heel natuurlijk recursief te schrijven, waarbij de aanroepstapel het onthouden doet. Vanaf A bezoekt recursieve DFS A, B, D, C, E, F. DFS bereikt elke knoop die BFS bereikt, maar het gevolgde pad is niet noodzakelijk het kortste.

function dfs(graph, u, seen = new Set(), order = []) {
  seen.add(u);
  order.push(u);
  for (const v of graph[u]) if (!seen.has(v)) dfs(graph, v, seen, order);
  return order;   // A B D C E F
}
Laden

Sleep om te draaien. Scroll of knijp om te zoomen.

Een doolhof als graaf: elk open vakje is een knoop, verbonden met aangrenzende open vakjes. Voer BFS en daarna DFS uit op hetzelfde doolhof en vergelijk de bezochte vakjes en de gevonden padlengte.

Welke kies je?

In het eerste doolhof van het lab bezoekt BFS 105 vakjes en vindt een pad van 22 zetten, het kortst mogelijke. DFS gaat toevallig richting het doel en bezoekt maar 31 vakjes, maar het pad kost 28 zetten. Geen van beide is altijd goedkoper. Beide verwerken elke knoop en elke kant een vast aantal keren en hebben dus looptijd O(V + E) voor V knopen en E kanten. Kies BFS als je de minste stappen nodig hebt: de minste zetten in een puzzel of de minste verbindingen tussen twee mensen. Kies DFS als je alles wilt verkennen of keuzes tot het einde wilt volgen: controleren of elke doolhofkamer verbonden is of puzzelzetten één reeks tegelijk proberen en teruggaan als je vastloopt. Als kanten verschillende lengten hebben, zoals wegen, is geen van beide op zichzelf genoeg; methoden zoals het algoritme van Dijkstra breiden het BFS-idee uit met afstanden.

Beide zoekmethoden moeten onthouden welke knopen al bezocht zijn. Anders zou een graaf met een kring (A naar B naar D naar C en terug naar A) het zoeken eindeloos laten rondgaan.
graafKnopen verbonden door kanten
knoopEen punt van een graaf
kantEen lijn die twee knopen verbindt
burenlijstVoor elke knoop de lijst met buren
wachtrijEerst erin, eerst eruit; gebruikt door BFS
BFSBreedte-eerst zoeken: ring voor ring; vindt paden met de minste kanten
DFSDiepte-eerst zoeken: eerst diep, dan terug; gebruikt een stapel
O(V + E)De looptijd van BFS en DFS
  1. Hoeveel kanten telt het kortste pad van A naar F in de voorbeeldgraaf?
  2. Hoeveel kanten heeft de voorbeeldgraaf?
  3. Welke gegevensstructuur gebruikt breedte-eerst zoeken?
    • Een wachtrij (eerst erin, eerst eruit)
    • Een stapel (laatst erin, eerst eruit)
    • Een gesorteerde lijst
    • Helemaal geen structuur
  4. Welke zoekmethode vindt gegarandeerd een pad met de minste kanten?
    • BFS
    • DFS
    • Beide, altijd
    • Geen van beide
  5. Zet de knopen op de bezoekvolgorde van BFS vanaf A (buren alfabetisch genomen).
  6. Zet de knopen op de bezoekvolgorde van recursieve DFS vanaf A (buren alfabetisch).
  7. Hoeveel zetten telt het kortste pad van S (linksboven) naar G (rechtsonder) op het rooster van 15 bij 9 in het lab, met alle muren weggehaald?
  8. Waarom moeten BFS en DFS onthouden welke knopen ze hebben bezocht?
    • Anders zou een kring in de graaf het zoeken eindeloos laten rondgaan
    • Om ze aan het einde af te drukken
    • Om ze te sorteren
    • Het is alleen nodig voor DFS
  9. In welk jaar loste Euler het bruggenprobleem van Koningsbergen op?
    • 1683
    • 1736
    • 1843
    • 1945
  10. Wat is de looptijd van BFS op een graaf met V knopen en E kanten?
    • O(V + E)
    • O(V × E)
    • O(log V)
    • O(2ⱽ)
  11. Je wilt het kleinste aantal vriend-van-een-vriendverbindingen tussen twee mensen in een sociaal netwerk weten. Welke zoekmethode?
    • BFS
    • DFS
    • Binair zoeken
    • Mergesort
Belangrijkste punten
  • Een graaf bestaat uit knopen verbonden door kanten; burenlijsten slaan die op.
  • BFS gebruikt een wachtrij, gaat ring voor ring en vindt paden met de minste kanten.
  • DFS gebruikt een stapel (of recursie), gaat eerst diep en dan terug.
  • Beide werken in O(V + E) en moeten bezochte knopen onthouden.
  • Met de oplossing van Euler uit 1736 voor de bruggen van Koningsbergen begon de grafentheorie.
Bronnen van deze cursus (11)
  • MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
  • lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
  • Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
  • with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
  • MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
  • D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
  • Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
  • Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
  • Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
  • Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
  • Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.

Geraadpleegd: 2026-10-17