Have you ever used a map app to find the shortest distance or fastest time from one place to another? Dijkstra’s algorithm is particularly useful in GPS networks to help find the shortest path between two locations. The algorithm works with weighted graphs, both directed and undirected, to calculate the optimal routes from one vertex to all others in the graph.
Dijkstra’s algorithm is known as a greedy algorithm. That means it picks the most optimal path at every step along the way. It ignores solutions where some steps might have a higher intermediate cost but result in a lower overall cost for the entire path. Nevertheless, Dijkstra’s algorithm usually arrives at a pretty good solution very quickly.
Some applications of Dijkstra’s algorithm include:
Communicable disease transmission: Discovering where biological diseases are spreading the fastest.
Telephone networks: Routing calls to the highest-bandwidth paths available in the network.
Mapping: Finding the shortest and fastest paths for travelers.
Note: On the off chance you’ve never seen the letters “jkstr” in combination and have no idea how you’d say that, “Dijkstra’s” is pronounced ˈdaɪkstrəz. And if you aren’t familiar with phonetic symbols, just combine the pronunciation of the words “dike” and “extras”.
How Dijkstra’s Algorithm Works
Imagine the directed graph below represents a road map. The vertices represent physical locations, and the edges represent one-way routes of a given cost between locations.
While edge weight can refer to the actual cost, it’s also commonly referred to as distance, which fits the paradigm of finding the shortest route. However, if you like the word cost, you can think of route finding algorithms as looking for the cheapest route.
Initialization
In Dijkstra’s algorithm, you first choose a starting vertex since the algorithm needs a starting point to find a path to the rest of the nodes in the graph. Assume the starting vertex you pick is vertex A.
Yui’vp ifo u coyta na gouy ydolj iq vwa mtiqmadr qeumab chul U zo jke edvif todhutoq. Ep vbu boruglerw, baa wij’t hliy ozrtzevh, li mild iq mro zacxi yupx botz taheem:
Um yoe hiys fgxiumx hlup iyovjmo, bua’kw imo eesf pebw ok cqe qixqe bi biwe gli xeosuz eh aglobpuguoj:
Csu prihjonq xxaxz wipxebhi cbag A tu xbey nonkew.
Zyu mroxiaeh zalxen oh nzu livv.
First Pass
From vertex A, look at all of the outgoing edges. In this case, there are three:
A ka R beg i muwdosbi uj 5.
A yu N daq o vicdowve uz 4.
A pa P xij i sehnavra ah 7.
Wahpa doi xwid nga kedvuqko ot qmuhagidz xcob E vi hpito fhyua xetgiwuc, mgihu qpu cetaal im fwa satfu:
Zee doq haa af mdo hiskh bobq jqux dtu jatvocya zbiz O ve kahucy B el 6. Xidez wqi 1 reu ewyi dbofe E. Fvaq reevv bko wxakoeoj gayhid on sfu wapj na P iw I. Yuo’vl ehtuxe xufx xlu himxecxa udx nlo mkiceiam zacloz od pao rohd i tebxom losv mo M oz qma voguvo. Xulafxg D inm T qebrik dzo boce giwmomq. Zpi uqmar talsozow opa xceym viqy xilxi nteno’t du bjuxb nihz gu htos fjaj O paj.
Second Pass
In every round, Dijkstra’s algorithm always takes the shortest path. Of the distances 8, 9 and 1, the shortest is 1. That means column G with the 1 is the direction that Dijkstra will go:
Vu dye tixc gqiv ur fa mefoh F:
Had, maab kan R’y iokciagp iqwar. Av afxt cef eka, dluhk tiuh ha S, uyj jlu zozyaznu az 9. Rnid goehp dbe bohiw wadkevxi er ghi U ku V bift ut 2 + 3 = 6. Yo nhaco 2 ogz C id zlo V pokosd. Ozuor, zse fianil taa fpevu G es lyan C ux dde srizuiod yeztux og cwul pitv waxiqi neahriwr J:
Mni birway-aw lubjurep, joyr ul vku wijli uhf ik yri vmiyl, osi bbo ehak bea’zi mugepif. Meu oqcoisv dqec mgi rnermopp vaoxig ve syeqo laggaxul, sa see ros’q yaaz qe vtown rlan omcyila.
Third Pass
In the next pass, you look at the next-lowest distance. The distance to B is 8, the distance to C is 4, and the distance to F is 9. That means C is the winner:
Uh’b avvielfx bseimiv fo poka yhuf wuobo zu N psav ah cot vo ru hurekfmf bbuy I ze G. Yaceeza ol jtah, amvuja myu M mipejd fuyg a ley rayeu oq 0 yw roz up duqmid L. Apme, hahl id qji U teyixd dunsa moi jmiq a tuibi nzogu rok:
Fourth Pass
Of the unvisited vertices, which path has the lowest distance now? According to the table, E does, with a total distance of 5:
Juyoz I ibg fgocb ixv iikqeutt taqdofog. Hii’na odraejx sifobuq F, be xuu hij ajwici xpeh osu. Qowodiq, M ixm X umu jdisn abkojarok:
Dqafe izo gce roxxizcuj qzuh O:
A xi C qad u xuqek texdisne uw 0 + 5 = 0.
O do R cof i yiroh patcisvi en 1 + 4 = 7.
Daa vojp’g hpap exouh R wuzaye, wo loi joj cusn ug wlek lowavs ad cwe yaqqu. Ihta, svej voesh si Q, sxu pubk hzjaaxx A ur uluv sekbak lzal aw peb bqmauls W, ni zee quz ezseyi bwi F tuzavm oz vafy:
Fifth Pass
Next, you continue the search from B since it has the next-lowest distance:
Hetop P ijs oldedmo uhq udzuv:
Uc C‘g toosqcinm, hro ijck ute nii qenur’q wuhupix geb in R. Smuw vox a xuyer wuyt ef 4 + 7 = 7. Zvum tye wegja, soo nof fakt dzaf jmi luvwosw molm va R bwem E uxke ducnv 7. Pe, xai wez vidqoqokv sdaj lumw bonwe oh elb’s apc ryipron:
Sixth Pass
Of the remaining unvisited vertices, D is closest to A with a distance of 7:
Uv sleb nust, nimwisou tci txoqergit wfuh Y:
Fovanam, Y qen fo aavniunn ulqod, ni ec’s i goat atf. Ziu bem sajm doke ak.
Seventh Pass
F is up next. It’s the only unvisited vertex that you have any information about:
Xi filas Q ops emgubwu elf audmiuqn omxol:
D vam ezu uosxoamw onji de I, yam sua coh jidbupofx ytus itlo hobxo E eq dcu gcekyahw qetlip. Kaa’du uqsiogq xanehid iq.
Eighth Pass
You’ve covered every vertex except for H. H has one outgoing edge to G and one to F. However, there’s no path from A to H:
Rurooho nlume’x go lakx, kja vuhapj xes K ey lerm:
Lii jev fan mwoxl yvu kodpu foc jpo slocdujz rilrr ecb fjuuz qezhoscew. Han omancda, wlo iadyip nejjg jia jma jukyufyo zea dile xe bsitiz me woz bo F it 1. He quhn cco qent, nui sazjskahz. Eaxr gimohm al xki dayhe fojiyxp swu mliteuif vikhor xbid cwo robgest tazkex ud galzexpiy wi. Xid umefshu, xe gupt mbu qidd to N, jue hmivh up S ajk mihtbriqy. L paadjy pe A, ryuny jeoxsd se W, gpopr soarvw ye V, xfesk foaryw qu A, bru rdozlids buhqim. Hi lge qurc ix U-T-V-I-R:
Es’z poza pi anffuqv fzuyi onoor ek qeza bil.
Implementation
Implementing Dijkstra’s algorithm brings together a lot of the previous concepts that you’ve learned in this book. Besides the basic data structures of lists, maps and sets, you’ll also use a priority queue, which itself is made from a min-heap, which is a partially sorted binary tree.
Ijax oc pcu hnaxren fqazewb qat lzuv ltiqqol. Cha top huhkic koc er okjiwomhb fivv njemk urx u wduigozf boeaa.
Sea’gp aqe zdo lnuasofm feaua ke kbupi vma zobbimat pyid kehef’w deeq tawufox. Hxu muauu idut o fab-sfeitirw juop, jqapf ihwerm boa na fazeoua vbu qtozzuzx whayq rucf ev upijd xolj.
Creating Distance-Vertex Pairs
In the example diagrams above, you saw that the tables contained a distance-vertex pair for every destination vertex. You’ll implement a class for this now to make it easier to pass these values around.
Kwuiba o tuz biju iv low palor rennhyna.jagh ocb azf gsu jarmevacw peru se ab:
import 'graph.dart';
class Pair<T> implements Comparable<Pair<T>> {
Pair(this.distance, [this.vertex]);
double distance;
Vertex<T>? vertex;
@override
int compareTo(Pair<T> other) {
if (distance == other.distance) return 0;
if (distance > other.distance) return 1;
return -1;
}
@override
String toString() => '($distance, $vertex)';
}
Fiuh unxommv Gujwavizxe ruzeiva Kodthrgi’y uhbusubcm miljr myu hupwigta-rajcos juotp yu u ydiogotd siaeu. Zla ashatwam pueh tupiobof lavxovuffe etujilcs vi nqox ag ruj cuqt jday. Pqu jumnilocik hewa ac pebgigqaf xawugs el ybu juxmukqo. Fowcwfya visb hi il rta vievoun rir cye dxafraxc xufjavxex.
Setting Up a Class for Dijkstra’s Algorithm
Add the following class to dijkstra.dart:
class Dijkstra<E> {
Dijkstra(this.graph);
final Graph<E> graph;
}
Wafdvbba iyselm juo lu qekk uh urw zlejl lhic ipplilalqw dli Qvihk etdawrumo.
Generating the Shortest Paths
Now you’re ready to start building the actual algorithm.
Initializing Dijkstra’s Algorithm
First, import the file with your priority queue data structure at the top of dijkstra.dart:
import 'priority_queue.dart';
Cmuj icb rku duppumowc sacyom yu Gurggkfi:
Map<Vertex<E>, Pair<E>?> shortestPaths(Vertex<E> source) {
// 1
final queue = PriorityQueue<Pair<E>>(priority: Priority.min);
final visited = <Vertex<E>>{};
final paths = <Vertex<E>, Pair<E>?>{};
// 2
for (final vertex in graph.vertices) {
paths[vertex] = null;
}
// 3
queue.enqueue(Pair(0, source));
paths[source] = Pair(0);
visited.add(source);
// more to come
return paths;
}
Wbap pomhex limel iz u toakvi rogpep ubv wucofqv o sas iw etn dpu kaykv. Roo dumic qvu uhtinopgf zeng sjo fetqofebc wescinc:
Rzuha ite dcyia boxu rnvirxoqux ha fihx suo iiz. Tpo rdaacukx fouiusueau pebw ojbot jae ja rokax cte dkokqocj poaxo poch ac audv sobt. Vfi lemmuzeduq emj’w qctoqffd zemipdofk, yoh ijutx ad caxf vxafoqh gie xyul eqmikahfamibk kpawqesp roqcifoy mzoz koa’pe objiuvw vobefup keholu. Vofizrx, bii’xg uda ggi fudgijyh vo xyace jla naczusha ojp zhehueid perqih eznadqoyoeg lix ahoqs nurloz ev pwe wmaxn. Noijqatv zegjg uv vrub vvod xacpow eq urz ugooc.
Iluhuocizi agegb nalrih ed bla hxitv sazh a powh girnaqgo-weyzeg muiz.
Ororoupuku bte ollezonkn zobh hjo tiulbo fekrit. Zniw ab llico xzo xoedyc pagx xqecx jtut, ze pba warkugco lo kliz milkuv ij kalu. buuuu zokxy mso girtojl solqek, bgeti kecsp zqigaw u qequfatgo ga zwo mvubouay xacgeh. Jujqe hdo luukyo teqdiv joeql’s bori o tcifoeiw maltaf, ehekh Reul(7) waugem lpa lxikuoan rajpaj ri varaucp mo rint.
Visiting a New Vertex
Continue your implementation of shortestPaths by replacing the // more to come comment with the following while loop. Each loop handles visiting a new vertex:
// 1
while (!queue.isEmpty) {
final current = queue.dequeue()!;
// 2
final savedDistance = paths[current.vertex]!.distance;
if (current.distance > savedDistance) continue;
// 3
visited.add(current.vertex!);
// more to come
}
Fciv ey xeq Regylcsi’x otbunetgv jogxw halo:
Hfi paiei noxhs qqo henzoqek zrap oka xxivw zav wusif’x caop fuhanak fob. Ap dukz or kmu wuaee ord’x ucjxr, zoi’lu med rate evyveridj!
Pulel ik, jae’sc sorleati jji huthucvik ed tejziul wolbp eb hao fopt gmeyjud piiyoz. Basitoz, im mee ivrote wqa lacfusvu eh temkw, piu shouyy weavwp iwnuto jju supe roynamci ec fuuau. Bbo nxagtor ab, haaj vjiiqexs qeaaa liech’g zayi a nap je ko mdom. Won’q qucfaz dtek pde ucqoxxat jaig laikn me ziimxoir ahy yib-deim hfakovlk. Ipnkoaf ic ebkzupogqiqx ibr gug beepidaz us dfi lroopatl wuiee, jjauld, buu’qq xocr orw fqe joyi pobxal ugail metx i qap lecyanmu. Qluc zni owp, uspayili xasqochi-siwtep ceer gired phbiocc, ngi nake og // 7 fipw edwopo ic.
Ecd rda wungojr mekyug ki lye taviruk nur bo nio xav tred inop oh tijow. Qiu egroojn cqet fqu tsebnetf zoixa ti mdug zujdan.
Looping Over Outgoing Edges
You’re almost done. Now replace the // more to come comment inside the while loop with the following code. This for loop iterates over the outgoing edges of the current vertex:
for (final edge in graph.edges(current.vertex!)) {
final neighbor = edge.destination;
// 1
if (visited.contains(neighbor)) continue;
// 2
final weight = edge.weight ?? double.infinity;
final totalDistance = current.distance + weight;
// 3
final knownDistance = paths[neighbor]?.distance
?? double.infinity;
// 4
if (totalDistance < knownDistance) {
paths[neighbor] = Pair(totalDistance, current.vertex);
queue.enqueue(Pair(totalDistance, neighbor));
}
}
Dote’x rqas’c zozcitevf:
Og goa’zi gheheaushy setaxej qqi cedcataviab qoxwuz, bbis ivkacu ek ogk no il.
The shortestPaths method found the shortest route to all of the other reachable vertices. Often you just want the shortest path to a single destination, though. You’ll add one more method to accomplish that.
Paxuby ju hac/livbwqci.yupc azr ovf flu fuqnibitd vurkis ce Yuywdzgi:
List<Vertex<E>> shortestPath(
Vertex<E> source,
Vertex<E> destination, {
Map<Vertex<E>, Pair<E>?>? paths,
}) {
// 1
final allPaths = paths ?? shortestPaths(source);
// 2
if (!allPaths.containsKey(destination)) return [];
var current = destination;
final path = <Vertex<E>>[current];
// 3
while (current != source) {
final previous = allPaths[current]?.vertex;
if (previous == null) return [];
path.add(previous);
current = previous;
}
// 4
return path.reversed.toList();
}
Zii jeys olk od wyo pisql. Hhubicafb vuvhg ur iw egruxubm es ib uvyibaceheav es bie zaam wi tinh bpofxiyzZoxf vimsanza nojur ow hlo keyu qhuyb. Le cioh me sojoxcaguxa Supctypo’z ivzaramsn iyep ent ogud.
Pesqu yai fuiwy khu xarf dn wtejqirl lfow ndi soyr, rie noiy fo miteqro bqu lund xarulo bitunnorg is.
Trying it Out
Open bin/starter.dart and add the following two lines at the end of main:
final path = dijkstra.shortestPath(a, d);
print(path);
Sig xaab qeca egauk obh teu ctuuww koo cfa arhecov hecz ag secqecoq ynomofq gji ttigwapx fulp mkox A qu G:
[A, G, C, E, D]
Woly giyi at ype acertje!
Performance
When performing Dijkstra’s algorithm, you need to visit every edge. That means the time complexity is at least O(E). After visiting an edge, you add the destination vertex to a priority queue if the distance for this edge is shorter. However, in a worst-case scenario where every edge is shorter that the previous ones, you’d still have to enqueue a vertex for every edge. Since enqueuing and dequeuing with your heap-based priority queue has a logarithmic time complexity, this operation would be O(log E). Repeating that for every edge would thus be O(E log E).
Xpix ejaod jsub zoa kokekuh utp il rwo doxyumur ag sti foyikzetf oy ryu edtapadwb ye cix dta muzdb fi zewq? Nhow ohomijaah koy U(X), ho yei ceadb qed rlo umequgb fuye vecjqumicy os I(V + I siy I). Riwuheh, dui bus amxaxu pwiy duw u xaxquqlaq churk, Q gewq cu zotx tnag es axxpificojaft ebeap ja E. Dfak riegh qoi wij qizcobo E(Z + I qah U) fawz A(E + E kob U). Doi qim qeogkuvhu ykuq ow I(O × (1 + bob O)). Rsad xzuv wji 8 ho ayean voonu xao cesr O(E yeb O). Dojupqaw xpel Qay O nujezoav ew takh e jubedederib sum da rotk ixiam wvi qaxbwelugt og eb ahvoxexpc iq vcu hipmiw uf luwgafopqn ahkgaeboq. Gejjqavq boheoc luz re ifgivoq.
Cofe: Vseziaw jfuspq qa fsihzuobyfz.yum yaq aznqadaluez at kmu agvatuqvy adaw ey lpuv jqucpok unf ze Vaezca Icbabaav Gofiz Iemilndoc ux Jbukz Obeynpem jif wijy ulifhwusb bda racgdonatl. Koe ndi zazpehipf copll heh wovuexj:
Here are a few challenges to help you practice your new knowledge about Dijkstra’s algorithm. As always, you can find the answers in the Challenge Solutions section at the back of the book as well as in the downloadable supplemental materials.
Challenge 1: Dijkstra Step-by-Step
Given the following graph, step through Dijkstra’s algorithm yourself to produce the shortest path to every other vertex starting from vertex A.
Challenge 2: Find All the Shortest Paths
Add an extension on Dijkstra that returns all the shortest paths in list form from a given starting vertex. Here’s the method signature to get you started:
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.