Grafrepresentasjoner og oppsett for traversering
Bygg rettede og urettede grafer med nabolister, initialiser BFS med en deque og DFS med en stakk eller rekursjon, og hold oversikt over besøkte noder.
Grafrepresentasjoner og oppsett for traversering er en gratis leksjon i DSA Interview Prep på CoddyKit. Dette er leksjon 1 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i DSA Interview Prep, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i DSA Interview Prep inneholder totalt 4 leksjoner.
Hva er en graf?
En graf er en samling av noder (hjørner) som er forbundet med kanter. I motsetning til trær kan grafer ha sykler, flere stier mellom noder og frakoblede komponenter. Grafer modellerer virkelige systemer som sosiale nettverk, veikart, avhengighetstrær og lenker mellom nettsider. Nesten alle intervjuer om systemdesign og algoritmer som går utover det trivielle, berører grafer – det er avgjørende å beherske representasjon og traversering.
# Graph terminology:
# - V: set of vertices (nodes)
# - E: set of edges
# - Directed graph: edges have direction (A -> B but not B -> A)
# - Undirected graph: edges are bidirectional
# - Weighted graph: edges have costs/weights
# - Cyclic: contains at least one cycle
# - Acyclic: no cycles (DAG = Directed Acyclic Graph)
# - Connected: every node reachable from every other
# - Disconnected: multiple isolated components
print('Graph: nodes + edges, directed/undirected, weighted/unweighted')Representasjon med naboliste
En naboliste lagrer listen over naboene til hver node. I Python brukes en dict som knytter hver node til en liste over tilstøtende noder. Dette er den vanligste representasjonen i intervjuprogrammeringsoppgaver: O(V + E) plass (effektivt for glisne grafer), O(degree) for å gå gjennom naboene og i gjennomsnitt O(1) for å sjekke naboskap med en variant som bruker et hash-sett. De fleste LeetCode-oppgaver om grafer bruker dette formatet.
from collections import defaultdict
# Build an undirected graph
def build_undirected(edges):
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
graph[v].append(u) # both directions
return graph
edges = [(0,1), (0,2), (1,3), (2,3), (3,4)]
graph = build_undirected(edges)
print(dict(graph))
# {0:[1,2], 1:[0,3], 2:[0,3], 3:[1,2,4], 4:[3]}
# Directed graph: only one direction
def build_directed(edges):
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v) # only u -> v
return graphRepresentasjon med nabomatrise
En nabomatrise er en V×V todimensjonal matrise der matrix[i][j] = 1 (eller kantvekten) hvis det finnes en kant fra i til j, og 0 ellers. Den gir oppslag av kanter på O(1), men bruker O(V²) plass uavhengig av antallet kanter – unødvendig mye for glisne grafer. Den foretrekkes når grafen er tett (har mange kanter), eller når det er avgjørende å kunne sjekke raskt om en kant finnes, for eksempel ved beregning av korteste veier mellom alle nodepar med Floyd-Warshall.
# Adjacency matrix for 5 nodes
V = 5
matrix = [[0] * V for _ in range(V)]
edges = [(0,1), (0,2), (1,3), (2,3), (3,4)]
for u, v in edges:
matrix[u][v] = 1
matrix[v][u] = 1 # undirected
# Print the matrix:
for row in matrix:
print(row)
# Neighbour check: O(1)
print('Edge 0-2:', bool(matrix[0][2])) # True
print('Edge 0-4:', bool(matrix[0][4])) # False
# Space: O(V^2) vs adjacency list O(V+E)
# Dense graph: matrix often better; sparse: list betterRepresentasjon med kantliste
En kantliste er den enkleste representasjonen: bare en liste med (source, destination)-tupler, eventuelt med vekter. Den bruker O(E) plass og gjør det enkelt å gå gjennom alle kanter. Det er imidlertid nødvendig å skanne alle kanter for å finne naboene til en node: O(E). Kantlister brukes i grafalgoritmer som går gjennom alle kanter et bestemt antall ganger, for eksempel Bellman-Ford (slakk alle kanter n-1 ganger) og Kruskals algoritme for minimumspenntre.
# Weighted edge list: (source, destination, weight)
edge_list = [
(0, 1, 4),
(0, 2, 1),
(1, 3, 1),
(2, 3, 5),
(3, 4, 3)
]
# Useful for:
# Bellman-Ford: iterate all edges n-1 times
# Kruskal's MST: sort by weight then union-find
# Sort by weight for Kruskal:
edge_list_sorted = sorted(edge_list, key=lambda e: e[2])
print('Sorted by weight:', edge_list_sorted)
# Finding neighbours: O(E) scan -- inefficient for traversal
node_0_neighbors = [v for u, v, w in edge_list if u == 0]
print('Node 0 neighbors:', node_0_neighbors)BFS-oppsett: kø og besøkt-sett
BFS (bredde-først-søk) utforsker en graf nivå for nivå ved hjelp av en kø. En viktig del er et besøkt-sett for å unngå å besøke noder på nytt i grafer med sykluser. Uten besøkt-settet vil BFS på en graf med sykluser kjøre i en uendelig løkke. Standardoppsettet er å initialisere køen med kildenoden, merke den som besøkt og deretter gjentatte ganger ta ut en node, behandle den og legge ubesøkte naboer i køen.
from collections import deque
def bfs(graph, start):
visited = {start} # mark source as visited
queue = deque([start]) # initialise queue
order = []
while queue:
node = queue.popleft()
order.append(node)
for neighbour in graph[node]:
if neighbour not in visited:
visited.add(neighbour) # mark BEFORE enqueue
queue.append(neighbour)
return order
from collections import defaultdict
graph = defaultdict(list)
for u, v in [(0,1),(0,2),(1,3),(2,3),(3,4)]:
graph[u].append(v); graph[v].append(u)
print(bfs(graph, 0)) # [0, 1, 2, 3, 4]DFS-oppsett: stakk eller rekursjon
DFS (dybde-først-søk) utforsker hver gren så langt som mulig før det går tilbake. Det kan implementeres rekursivt (ved hjelp av kallstakken) eller iterativt (ved hjelp av en eksplisitt stakk). Begge variantene krever et besøkt-sett for grafer med sykluser. Den iterative varianten legger naboene på stakken i omvendt rekkefølge for å samsvare med traverseringsrekkefølgen til rekursiv DFS, selv om utforskningsrekkefølgen kan variere mellom de to implementasjonene.
def dfs_recursive(graph, node, visited=None, order=None):
if visited is None: visited = set(); order = []
visited.add(node)
order.append(node)
for neighbour in graph[node]:
if neighbour not in visited:
dfs_recursive(graph, neighbour, visited, order)
return order
def dfs_iterative(graph, start):
visited = set()
stack = [start]
order = []
while stack:
node = stack.pop()
if node in visited: continue
visited.add(node)
order.append(node)
for neighbour in reversed(graph[node]): # reverse for same order as recursive
if neighbour not in visited:
stack.append(neighbour)
return order
print('Recursive DFS:', dfs_recursive(graph, 0))
print('Iterative DFS:', dfs_iterative(graph, 0))Når BFS bør brukes fremfor DFS
Velg BFS når De trenger den korteste veien (færrest kanter) i en uvektet graf, eller når noder må behandles nivå for nivå. Velg DFS når De trenger å utforske alle nåbare noder, oppdage sykluser, finne sammenhengende komponenter, utføre topologisk sortering eller finne alle veier. I praksis: BFS for «korteste/minste antall hopp» og DFS for «eksistens/nåbarhet/opplisting».
# BFS use cases:
# - Shortest path in unweighted graph (fewest edges)
# - Level-order traversal
# - Word ladder (minimum transformations)
# - Clone graph
# DFS use cases:
# - Connected components (flood fill)
# - Cycle detection
# - Topological sort
# - All paths between two nodes
# - Maze solving (any path)
# - N-queens, Sudoku (backtracking)
# Both: O(V + E) time, O(V) space for visited
print('BFS: shortest hops | DFS: existence and enumeration')Graf fra LeetCodes inndataformater
Grafoppgaver på LeetCode kan ha ulike inndataformater. Kantliste: [[0,1],[0,2]] – bygg en naboliste. Indeksbasert naboliste: graph[i] er listen over naboene til i. Rutenett/matrise: en m×n todimensjonal matrise der cellene er noder, og tilstøtende celler (opp/ned/venstre/høyre) er naboer. Node med barn: egendefinerte klasser som Node(val, neighbors). Gjenkjenn disse formatene og konverter dem til en naboliste som første trinn.
# Format 1: edge list -> adjacency list
def edges_to_adj(n, edges):
graph = [[] for _ in range(n)]
for u, v in edges:
graph[u].append(v)
graph[v].append(u)
return graph
# Format 2: 2D grid -> adjacency (implicit)
# Neighbours of (r, c): (r-1,c), (r+1,c), (r,c-1), (r,c+1)
DIRS = [(-1,0),(1,0),(0,-1),(0,1)]
def grid_neighbours(grid, r, c):
rows, cols = len(grid), len(grid[0])
return [(r+dr, c+dc) for dr, dc in DIRS
if 0 <= r+dr < rows and 0 <= c+dc < cols]
grid = [[1,1,0],[0,1,1],[1,0,0]]
print('Neighbours of (0,0):', grid_neighbours(grid, 0, 0))
print('Neighbours of (1,1):', grid_neighbours(grid, 1, 1))Merking av besøkte celler i rutenett
For rutenettoppgaver finnes det to måter å holde oversikt over besøkte celler på. Alternativ A: bruk et separat visited-sett med (row, col)-tupler – O(m*n) ekstra plass. Alternativ B: endre rutenettet direkte ved å merke besøkte celler med en spesialverdi (for eksempel '#' eller 2) og gjenopprette dem etterpå ved behov. Den direkte metoden bruker O(1) ekstra plass og er vanlig i oppgaver om flomfylling og antall øyer.
def num_islands(grid):
if not grid:
return 0
rows, cols = len(grid), len(grid[0])
count = 0
def dfs(r, c):
if r < 0 or r >= rows or c < 0 or c >= cols:
return
if grid[r][c] != '1':
return
grid[r][c] = '#' # mark as visited (in-place)
dfs(r+1, c); dfs(r-1, c)
dfs(r, c+1); dfs(r, c-1)
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1':
dfs(r, c)
count += 1
return count
grid = [['1','1','0','0'],
['1','1','0','0'],
['0','0','1','0'],
['0','0','0','1']]
print(num_islands(grid)) # 3Initialisering av BFS med flere kilder
BFS med flere kilder starter fra flere noder samtidig ved å initialisere køen med alle kildenodene, som merkes som besøkt. Dette brukes i oppgaver som 'distance from nearest 0', 'rotting oranges' og 'walls and gates', der målet er å finne den korteste avstanden fra en hvilken som helst av kildenodene. BFS med flere kilder har kjøretid O(V + E) – det samme som BFS med én kilde – fordi hver node fortsatt besøkes høyst én gang.
from collections import deque
def rotting_oranges(grid):
rows, cols = len(grid), len(grid[0])
queue = deque()
fresh = 0
# Multi-source: all rotten oranges start at time=0
for r in range(rows):
for c in range(cols):
if grid[r][c] == 2:
queue.append((r, c, 0)) # (row, col, time)
elif grid[r][c] == 1:
fresh += 1
dirs = [(0,1),(0,-1),(1,0),(-1,0)]
time = 0
while queue:
r, c, t = queue.popleft()
for dr, dc in dirs:
nr, nc = r+dr, c+dc
if 0<=nr<rows and 0<=nc<cols and grid[nr][nc]==1:
grid[nr][nc] = 2 # mark rotten
fresh -= 1
queue.append((nr, nc, t+1))
time = t + 1
return time if fresh == 0 else -1
print(rotting_oranges([[2,1,1],[1,1,0],[0,1,1]])) # 4Grafens tetthet og valg av representasjon
Valget mellom naboliste og matrise avhenger av grafens tetthet – forholdet E/V². En glissen graf (E << V²) har fordel av nabolister: O(V+E) plass mot O(V²) for en matrise. En tett graf (E ≈ V²) har fordel av nabomatriser: oppslag av kanter på O(1) mot O(degree) for lister. I intervjuprogrammeringsoppgaver er nabolister nesten alltid det riktige valget, siden de fleste oppgaver omhandler glisne grafer.
# Graph density comparison:
# Sparse: social network (V=1B users, avg 200 friends)
# E = 200 * 1B = 200B << V^2 = 10^18 -> adjacency list
# Dense: complete graph (every node connected to every other)
# E = V*(V-1)/2 ≈ V^2 -> adjacency matrix
# Interview rule of thumb:
# - Default to adjacency list (defaultdict(list))
# - Use matrix only when asked about dense graph or O(1) edge lookup
# - Grid problems: use implicit adjacency (4-directional neighbours)
print('Sparse graph (E << V^2): use adjacency list')
print('Dense graph (E ~ V^2): consider adjacency matrix')Hurtigsjekk
Test forståelsen Deres av konseptene fra Data Structures & Algorithms — Coding Interview Prep i denne leksjonen.
Oppsummering av leksjonen
I denne leksjonen har De lært om: tre grafrepresentasjoner (naboliste, matrise og kantliste) og når hver av dem bør velges, oppsett av BFS og DFS med besøkt-sett for å unngå uendelige løkker i grafer med sykluser, samt praktiske mønstre som direkte merking av rutenett og BFS med flere kilder. Neste tema er å bruke BFS til å finne korteste veier og traversere nivåer.
Lær deg Python med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 30
- Leksjoner
- 120
Ofte stilte spørsmål
Er leksjonen «Grafrepresentasjoner og oppsett for traversering» gratis?
Ja – du kan lese valgfritt 3 av leksjonene i læringsstien DSA Interview Prep, inkludert «Grafrepresentasjoner og oppsett for traversering», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i DSA Interview Prep inneholder totalt 4 leksjoner.
Hva lærer jeg i «Grafrepresentasjoner og oppsett for traversering»?
Bygg rettede og urettede grafer med nabolister, initialiser BFS med en deque og DFS med en stakk eller rekursjon, og hold oversikt over besøkte noder. Du øver på DSA Interview Prep med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med DSA Interview Prep?
Ingen tidligere erfaring er nødvendig. DSA Interview Prep på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 1 av 4.
Hvor lang tid tar leksjonen «Grafrepresentasjoner og oppsett for traversering»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne DSA Interview Prep-leksjonen?
Ja. Alle DSA Interview Prep-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Grafrepresentasjoner og oppsett for traversering
- BFS: korteste sti og nivågjennomgang
- DFS: sammenhengende komponenter og flood fill
- Syklusdeteksjon i rettede og urettede grafer