Menu
Coddy logo textTech

Algoritmo di Dijkstra

Ultimo aggiornamento

L'algoritmo di Dijkstra trova il cammino minimo da un nodo sorgente a tutti gli altri nodi in un grafo con pesi degli archi non negativi. Tiene una distanza provvisoria per ogni nodo, fissa ripetutamente il nodo non ancora fissato con la distanza provvisoria più piccola e ne rilassa gli archi, aggiornando la distanza di un vicino ogni volta che trova un percorso più breve passando per il nodo corrente. Premi play qui sopra per vedere le distanze scendere man mano che ogni nodo viene fissato.

L'idea chiave è greedy: una volta scelto il nodo non fissato più vicino, la sua distanza è definitiva, perché qualsiasi altro percorso verso di lui dovrebbe passare per un nodo che è già più lontano. Con una coda di priorità basata su heap binario, Dijkstra richiede tempo O((V + E) log V). Richiede pesi non negativi: usa Bellman-Ford se gli archi possono essere negativi.

Complessità temporale e spaziale

ImplementazioneComplessitàNote
Heap binarioO((V + E) log V)La scelta comune e pratica
Scansione dell'arrayO(V²)Più semplice; va bene per grafi densi
SpazioO(V)Distanze + coda di priorità
RichiedePesi non negativiGli archi negativi rompono la scelta greedy

Passo dopo passo

PassoCosa succede
1Imposta la distanza della sorgente a 0 e tutte le altre a infinito.
2Scegli il nodo non fissato con la distanza provvisoria più piccola.
3Segnalo come fissato: la sua distanza minima ora è definitiva.
4Per ogni vicino, calcola la distanza attraverso il nodo corrente + il peso dell'arco.
5Se è minore della distanza attuale del vicino, rilassalo.
6Ripeti finché tutti i nodi raggiungibili non sono fissati.

Esempio svolto

Cammini minimi dalla sorgente A sul grafo con gli archi A-B=4, A-C=1, C-B=2, C-D=5, B-D=1:

PassoFissaDistanzeAzione
0-A=0, B=∞, C=∞, D=∞Inizializza: sorgente A=0, tutte le altre a infinito.
1A (0)B=4, C=1, D=∞Rilassa gli archi da A: imposta B=4, C=1.
2C (1)B=3, D=6Passando per C: B=1+2=3 batte 4; D=1+5=6.
3B (3)D=4Passando per B: D=3+1=4 batte 6.
4D (4)A=0, C=1, B=3, D=4Fissa D; non resta nulla da rilassare. Fatto.

Quando usare l'algoritmo di Dijkstra

Usalo quandoEvitalo quando
Tutti i pesi degli archi sono non negativiUn arco qualsiasi può essere negativo: usa Bellman-Ford
Ti servono i cammini minimi da una sorgente a tutti i nodiTi servono i cammini minimi tra tutte le coppie: Floyd-Warshall è più semplice
Il grafo è pesato e vuoi distanze esatteIl grafo non è pesato: una semplice BFS è più veloce e più semplice
Hai a disposizione un buon heap o una buona coda di prioritàVuoi raggiungere in fretta un'unica destinazione con un'euristica: usa A*

Codice Dijkstra's Algorithm

Un'implementazione di Dijkstra's Algorithm pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.

Codice Dijkstra's Algorithm in Python

Python
1import heapq2
3
4def dijkstra(graph, start):5    dist = {node: float("inf") for node in graph}6    dist[start] = 07    pq = [(0, start)]8    while pq:9        d, node = heapq.heappop(pq)10        if d > dist[node]:11            continue  # stale entry, a shorter path was already found12        for neighbor, weight in graph[node]:13            new_dist = d + weight14            if new_dist < dist[neighbor]:15                dist[neighbor] = new_dist16                heapq.heappush(pq, (new_dist, neighbor))17    return dist18
19
20graph = {21    "A": [("B", 4), ("C", 1)],22    "B": [("D", 1)],23    "C": [("B", 2), ("D", 5)],24    "D": [("E", 3)],25    "E": [],26}27
28for node, d in dijkstra(graph, "A").items():29    print(f"A -> {node}: {d}")
Esegui questo codice nel playground Python

Domande frequenti sull'algoritmo di Dijkstra

Qual è la complessità temporale dell'algoritmo di Dijkstra?
Con una coda di priorità basata su heap binario richiede O((V + E) log V). Una versione semplice che a ogni passo scorre un array per trovare il minimo è O(V²), che sui grafi densi può essere perfino più veloce. Entrambe usano spazio O(V).
Perché Dijkstra non funziona con pesi negativi?
Dijkstra presume che, una volta fissato il nodo non fissato più vicino, quella distanza sia definitiva. Un arco negativo potrebbe creare in seguito un percorso più breve verso un nodo già fissato, violando questa ipotesi. Per i grafi con pesi negativi usa l'algoritmo di Bellman-Ford.
Qual è la differenza tra Dijkstra e BFS?
La BFS trova i cammini minimi contando gli archi (ogni arco pesa di fatto 1) usando una coda semplice. Dijkstra generalizza questa idea ai grafi pesati espandendo sempre il nodo con la distanza totale più piccola, grazie a una coda di priorità. Su un grafo non pesato i due producono gli stessi percorsi.
Qual è la differenza tra Dijkstra e la ricerca A*?
A* è Dijkstra più un'euristica che stima la distanza rimanente verso una destinazione, così guida la ricerca verso quell'obiettivo invece di espandersi in modo uniforme in tutte le direzioni. Quando l'euristica vale zero, A* diventa esattamente Dijkstra. Usa A* quando hai un'unica destinazione e una buona euristica ammissibile; usa Dijkstra quando ti servono le distanze verso ogni nodo.
Quando conviene usare Dijkstra invece di Bellman-Ford?
Usa Dijkstra ogni volta che tutti i pesi degli archi sono non negativi: è più veloce, O((V + E) log V) contro O(V·E) di Bellman-Ford. Scegli Bellman-Ford solo quando gli archi possono essere negativi o devi rilevare cicli negativi. Sui grafi con pesi non negativi Dijkstra è quasi sempre la scelta migliore.
Dijkstra può rivisitare un nodo dopo averlo fissato?
No: una volta fissato un nodo, la sua distanza è definitiva e non viene più rilassato. Una trappola comune nelle implementazioni con heap è lasciare voci vecchie nella coda di priorità dopo che la distanza di un nodo è migliorata; devi saltare un nodo estratto se è già fissato (la sua distanza estratta supera quella registrata). Dimenticare questo controllo dà comunque risposte corrette, ma spreca lavoro.
Illustrazione dei linguaggi di programmazione di Coddy

Padroneggia gli algoritmi con Coddy

INIZIA