DSA Interview Prep · Lektion

Floyd-Warshall: Korteste veje mellem alle par

Udfyld afstandsmatricen mellem alle par med Floyd-Warshall-algoritmen med tre indlejrede løkker, og anvend den til at finde det mindste antal hop mellem alle nodepar.

Lektion 3 af 413 trin

Floyd-Warshall: Korteste veje mellem alle par er en gratis DSA Interview Prep-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i DSA Interview Prep, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. DSA Interview Prep-kurset indeholder 4 lektioner i alt.

Korteste stier mellem alle nodepar

Floyd-Warshall beregner korteste stier mellem alle par af noder i en vægtet graf — inklusive grafer med negative kantvægte (men ikke negative cyklusser). At køre Dijkstra fra hver kilde tager O(V × (V+E) log V); Floyd-Warshall kører i O(V³)

Den grundlæggende idé: Mellemliggende noder

Floyd-Warshalls indsigt: dp[i][j][k] = den korteste sti fra i til j, hvor kun noderne {0, 1, ..., k} bruges som mellemled. Enten bruger den korteste sti node k som mellemled, eller også gør den ikke. Hvis den gør: dp[i][j][k] = dp[i][k][k-1] + dp[k][j][k-1]. Hvis ikke: dp[i][j][k] = dp[i][j][k-1]. Da den tredje dimension kun bevæger sig fremad, kan den elimineres — vi opdaterer direkte.

Initialisering af afstandsmatricen

Start med en V×V-matrice: dist[i][i] = 0 (afstand fra en node til sig selv er nul), dist[i][j] = weight for direkte kanter og dist[i][j] = inf for par uden kanter. Gennemløb derefter alle mellemliggende noder k, og opdatér parrene (i, j). Den ydre løkke over k skal komme først, så vi korrekt opbygger stier gennem et stadigt større sæt af tilladte mellemled.

def floyd_warshall(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V):
        dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w  # directed graph
    
    for k in range(V):       # intermediate node
        for i in range(V):
            for j in range(V):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    
    return dist

Komplet implementering med eksempel

Lad os gennemgå Floyd-Warshall på en graf med 4 noder. Efter behandling af hver mellemliggende node k udfyldes matricen med kortere stier, der går gennem node k. Algoritmen håndterer naturligt flere kanter ved gradvist at opbygge de korteste stier.

def floyd_warshall(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V):
        dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w
    for k in range(V):
        for i in range(V):
            for j in range(V):
                if dist[i][k] != INF and dist[k][j] != INF:
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
    return dist

V = 4
edges = [(0,1,3),(0,2,7),(1,2,1),(1,3,5),(2,3,2)]
dist = floyd_warshall(V, edges)
for row in dist:
    print([x if x != float('inf') else 'INF' for x in row])

Registrering af negative cyklusser

Efter Floyd-Warshall er kørt, skal du kontrollere hoveddiagonalen: Hvis en dist[i][i] < 0, findes der en negativ cyklus, der går gennem node i. Det skyldes, at en negativ cyklus gør det muligt at nå i fra i med negativ omkostning. Hvis der ikke findes en negativ cyklus, forbliver alle diagonale elementer 0.

def has_negative_cycle_fw(V, edges):
    dist = floyd_warshall(V, edges)
    for i in range(V):
        if dist[i][i] < 0:
            return True  # negative cycle through node i
    return False

# Negative cycle: 0->1->2->0 with weights 1,-3,1 (sum=-1)
edges_neg = [(0,1,1),(1,2,-3),(2,0,1)]
print(has_negative_cycle_fw(3, edges_neg))  # True

Genskabelse af stien

For at genskabe den faktiske sti fra i til j skal du vedligeholde en next[i][j]-matrix: Oprindeligt er next[i][j] = j for direkte kanter. Ved en opdatering via den mellemliggende node k skal du sætte next[i][j] = next[i][k]. For at genskabe stien skal du starte ved i og følge next-pegerne, indtil du når j. Det tilføjer O(V²) plads og O(V) pr. stigenskabelse.

def fw_with_path(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    nxt = [[None]*V for _ in range(V)]
    for i in range(V): dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w; nxt[u][v] = v
    for k in range(V):
        for i in range(V):
            for j in range(V):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
                    nxt[i][j] = nxt[i][k]
    return dist, nxt

def get_path(nxt, i, j):
    if nxt[i][j] is None: return []
    path = [i]
    while i != j:
        i = nxt[i][j]; path.append(i)
    return path

Transitiv lukning

En enklere variant: transitiv lukning besvarer spørgsmålet 'kan node j nås fra node i?' for alle par. Erstat afstande med booleske værdier: reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j]). Dette er Floyd-Warshall med boolesk OR i stedet for addition og min. Initialisér reach[i][i] = True og reach[i][j] = True for direkte kanter.

def transitive_closure(V, edges):
    reach = [[False]*V for _ in range(V)]
    for i in range(V):
        reach[i][i] = True
    for u, v, _ in edges:
        reach[u][v] = True
    for k in range(V):
        for i in range(V):
            for j in range(V):
                reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j])
    return reach

edges = [(0,1,1),(1,2,1)]
R = transitive_closure(3, edges)
print(R[0][2])  # True (0 can reach 2 via 0->1->2)

Kompleksitet og hvornår den skal bruges

Floyd-Warshall: O(V³)-tid, O(V²)-plads. For tætte grafer (E ≈ V²) med V ≤ 300 er dette hurtigere end at køre Dijkstra V gange (også O(V³) i det tilfælde). For sparsomme grafer med V = 1000 og E = 3000 koster V Dijkstra-kørsler O(V×E×log V) ≈ 33M, mens Floyd-Warshall koster O(V³) = 10⁹ — Dijkstra vinder. Du skal vide, hvornår hver algoritme er passende.

Mindste antal hop mellem alle par

Sæt alle kantvægte til 1 (eller brug en boolesk nabomatrix med Floyd-Warshall, hvor du bruger addition i stedet for min): dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]). Dette beregner det mindste antal hop mellem alle par — det samme resultat som BFS mellem alle par, men beregnet med ét O(V³)-gennemløb med Floyd-Warshall.

def min_hops_all_pairs(V, adj_list):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V):
        dist[i][i] = 0
        for j in adj_list[i]:
            dist[i][j] = 1
    for k in range(V):
        for i in range(V):
            for j in range(V):
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
    return dist

adj = [[1,2],[2],[3],[],[]]
print(min_hops_all_pairs(5, adj)[0])  # [0, 1, 1, 2, INF]

Interviewkontekst: Når interviewere spørger om Floyd-Warshall

Floyd-Warshall dukker op i interviews om opgaver, der handler om: (1) afstande mellem alle par i en lille graf, (2) at finde ud af, om der findes en cyklus med negativ samlet vægt, (3) at beregne korteste veje i problemer med begrænsningsudbredelse og (4) opgaver, der udtrykkeligt beder om løsninger i O(V³), hvor V ≤ 200. Nævn altid strukturen med tre løkker og kravet om, at der ikke må være negative cyklusser, for at algoritmen er korrekt.

Urettede grafer med Floyd-Warshall

For urettede grafer skal du tilføje begge retninger for hver kant: dist[u][v] = dist[v][u] = weight. Resten af algoritmen er identisk. Den resulterende matrix er symmetrisk: dist[i][j] == dist[j][i] for alle par. Vær forsigtig under initialiseringen, så du ikke ved en fejl tildeler retningsbestemte kanter — urettede kanter skal tilføjes i begge retninger til den indledende matrix, før de tre løkker køres.

def fw_undirected(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V): dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w
        dist[v][u] = w  # both directions for undirected
    for k in range(V):
        for i in range(V):
            for j in range(V):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    return dist

Hurtigt tjek

Afprøv din forståelse af begreberne fra Data Structures & Algorithms — Coding Interview Prep i denne lektion.

Opsummering af lektionen

I denne lektion har du lært: Floyd-Warshall beregner de korteste veje mellem alle par med tre indlejrede løkker og rekurrensen dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]), negative cyklusser kan registreres ved at kontrollere, om en dist[i][i] < 0 efter afslutningen, og algoritmen kører i O(V³)-tid og bruger O(V²)-plads. Næste gang genbesøger vi anvendelser af korteste veje med Network Delay Time og teknikker til rekonstruktion af veje.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Floyd-Warshall: Korteste veje mellem alle par” gratis?

Ja — alle 3 lektioner i læringssporet DSA Interview Prep, inklusive “Floyd-Warshall: Korteste veje mellem alle par”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. DSA Interview Prep-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Floyd-Warshall: Korteste veje mellem alle par”?

Udfyld afstandsmatricen mellem alle par med Floyd-Warshall-algoritmen med tre indlejrede løkker, og anvend den til at finde det mindste antal hop mellem alle nodepar. Du øver dig i DSA Interview Prep med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på DSA Interview Prep?

Der kræves ingen tidligere erfaring. DSA Interview Prep på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Floyd-Warshall: Korteste veje mellem alle par”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne DSA Interview Prep-lektion?

Ja. Alle DSA Interview Prep-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Dijkstras algoritme med en prioritetskø
  2. Bellman-Ford og negative cykler
  3. Floyd-Warshall: Korteste veje mellem alle par
  4. Netværksforsinkelse og rekonstruktion af sti
← Tilbage til DSA Interview Prep