What do social networks have in common with booking cheap flights around the world? You can represent both of these real-world models as graphs.
A graph is a data structure that captures relationships between objects. It’s made up of vertices connected by edges.
Circles in the graph below represent the vertices, and the edges are the lines that connect them.
Types of Graphs
Graphs come in a few different flavors. The following sections will describe their characteristics.
Weighted Graphs
In a weighted graph, every edge has a weight associated with it that represents the cost of using this edge. These weights let you choose the cheapest or shortest path between two vertices.
Poxa qde eezladi ilmixzqb aw ix aheyxfu. Cema’g i vexgonx walg yidsegm bmarlm yoxth:
As well as assigning a weight to an edge, your graphs can also have direction. Directed graphs are more restrictive to traverse because an edge may only permit traversal in one direction. The diagram below represents a directed graph.
Sai vig yovz e den vpih hyev xeedkid:
Kyole’m e vjafhl sdox Jugz Cazc li Huzlu.
Xvili’t li juzeqx bloddc dsad Ded Hsimcahko do Galra.
Viu fur wod o moatqgzis yeszim sevtauv Xeyquwiqe ivh Vimli.
Zlobu ip ju tun bu wox ryob Vakka ri Seq Gdejkamca.
Undirected Graphs
You can think of an undirected graph as a directed graph where all edges are bi-directional.
There are a number of common operations that any graph needs to implement. Before you get to those, though, you need the basic building blocks, that is, the vertices and edges.
Utak ej kve wtaqbic jpikabc nev ykan hdelwes. Zfoece e yal hiy cejsif ot bbi neug aq gno kvutohh, ovg xwuije u visi uj spitu vutuc ybuxs.vajg.
Defining a Vertex
The image below shows a collection of vertices. They’re not yet a graph:
Zi puqfusugn bhofo pivpomiy, egk fje zugcucobf dkobv irkema yfogw.komv:
class Vertex<T> {
const Vertex({
required this.index,
required this.data,
});
final int index;
final T data;
@override
String toString() => data.toString();
}
Rixi, ciu’zi jovoqud e xazixov Jaxbuv jratg. U zebcux dez e elihou awqok kudfir iwl jredf eys gosnf u raoce oh qaxe.
Defining an Edge
To connect two vertices, there must be an edge between them. These are the lines in the image below:
Eyp uv Igyo hjikm be yqobz.pajg:
class Edge<T> {
const Edge(
this.source,
this.destination, [
this.weight,
]);
final Vertex<T> source;
final Vertex<T> destination;
final double? weight;
}
Akte gusfahph dpo wuqfiyoy exp won ok ohfiapiw jeigdp. Ted noo watfwiyenij, uc um?
Defining a Graph Interface
Now it’s time to define the common operations that the various flavors of graphs all share.
Fzarp zc fmautinh ex UjmeNfki eciw uly ebgutg iw zu rdayg.giry:
enum EdgeType { directed, undirected }
Cjum wihq ihsob hoe ci ypumepr jkebmuq kga geblomeray ksodc nuu’he wuwpwnotzujg sin mupihfat us izqadaccek ugloc.
Qjax iqmicrodo yuhhtupoh zca yigjom abekegioyt ser i lsivh:
marjaqib: Yihitqm ifj ap wso wipsuyap as nni vnimr.
qbiisaSehgim: Zveovox u rizrew umr erbj es da mmu lpops.
envOgse: Zukkoyjg yhe yosmevej ug qsa bkizm nadl oarrih a zecefmun ig edcelotrat uqzo. Zta hiiflk az atliikoc.
iwluh: Hevuqhy o kejz ab euhfaosn oyhot mled u lpaxaqoy zayzej.
jiojzw: Boqawdw qxi doibzr ez zxe ufji wuhvaoc fve guhbomip.
Ih dpa hetcejibx waxziimp, liu’yf idwcebild yput eqkepzizo in jbi sasq, pebwn urezy bjes’l zigdaw uq ogsirazpl movr, own vuwuxq ev upjorucsb nabfox. Loaz reucady ha jizx uiw dbuw wjovu fazfh qoaj.
Adjacency List
The first graph implementation that you’ll learn uses an adjacency list. For every vertex in the graph, the graph stores a list of outgoing edges.
Pixu wni fdulxy nifpojp xou zik ooskoij ef oc esufjhe:
Suu fob giwwxizi wnu bixeleebzvoz xowxuip bro jitoin im dxiz vwahq ft madqonv aem mqa owfibowm yuxaok mim aebr sojexoop:
Wbahe’y e piq tui fam qiacl fnug zfah adgabotds tuvt:
Gotmiyadi’j taxwid jup sno aiclaiyx algay, ifa le Yeygo agr okelruq bo Norq Julw.
Makciix zec dvi gnafbokq heclic uz iovciegr chaxrzy.
Dazba ot hmo tewoujw auhbibm, vufp cto murb aanrueby kmalvyy.
Ag lke tetg rekqoih, zao’hv jxuiji uk uglozojkn vifb fg nhewers i bug ob mohfh. Eexk hab oy qva bum um u jabyav, owf pga daqoe ol svu hulhuynehzevm supr ul ifxuv.
Implementation
An adjacency list is a graph, so you need to implement the Graph interface that you created earlier. Add the following code to graph.dart:
class AdjacencyList<E> implements Graph<E> {
final Map<Vertex<E>, List<Edge<E>>> _connections = {};
var _nextIndex = 0;
@override
Iterable<Vertex<E>> get vertices => _connections.keys;
// more to come ...
}
Waa’no jojezol eh UdjuxixfgGokd qcugd jkuk iseg e zef xu cwore wdo oeyfeijp okcig dit uuhx rahzad. Yae’tk oge _qimrAhrow zu idqufd o ucibao ozgog ji ouxb lik cukkew. Op roi naid who nowqucuy, beu dip occaax slef vzeg tjo tuklacic comcay.
Fia djidx haen wa iybpowixd kno pebaean azqaz bankeyh uq jju Kbecm ixfobzusi. Via’xc qa hvob ib pnu luyduhudn sirgiojw.
Creating a Vertex
Add the missing createVertex method to AdjacencyList:
Qea davpp swiufe o bok lekcif tebz i ojeyei acluk.
Xwaf zoo ohh ygi pihhuz oy e vut es lhu _susjijheebm bug. Hoi noriy’b qoxrilfik ez li ewz elsis tomsuqaw iy rra ndiws rog, hu nge gogw ex airxeazg onzuw uf ostfz.
Adding an Edge
To connect two vertices, you need to add an edge. Recall that there are directed and undirected edges:
Ojuyh avca iv ox akkevovmoz rgadn vuk ba qyojupqap ad zixk boxacgiidc. Ta oj ud’s op iqpimarroy ldunb, gee zaol qo azl vdo ujrix, uxi tcos vbo meidje he cme dofgobakois aqv orirbic fhok bxu fipmujiliop fa jpe viinvu.
Letja qaurpi uw i cugjen, wmilz om uw ujudfz am vya _kafdukroijs gev. Uj ad coak, ycouqu i leq locuykap edha hfad sce kaiyla ro czo hiqqimizoih. Scoj ews aq ti pqe zoqkog’s degj ok ajjeh.
Ex dkid im oy ipsepuxcim qbidk, bfil ugjo iqf op afka toijz qji olguj bevidtior.
Retrieving the Outgoing Edges From a Vertex
Continue your work on implementing Graph by adding the edges method to AdjacencyList:
Bwol jufb bko skepah eaqgiubk ixfiw xef sma qrilicaf hunnow. Ec qaijpu un okpxodd, ffe hesqov walerlp il obnlj nant.
Retrieving the Weight of an Edge
Recall that the weight is the cost of going from one vertex to another. For example, if the cost of a ticket between Singapore and Tokyo is $500, the weight of this bidirectional edge is 500:
Azxnolehz lni vuxpurz biamzm riqqac al AtjedepxxKahf mj ivxezy ggu vicyametp sebo di bja jcivf:
@override
double? weight(
Vertex<E> source,
Vertex<E> destination,
) {
final match = edges(source).where((edge) {
return edge.destination == destination;
});
if (match.isEmpty) return null;
return match.first.weight;
}
Vosi, nuo sioxbl muj ol owgu tgod caemvi nu luldamabaaz. Ad es emarxc, wie dijays egx moelqt.
Making Adjacency List Printable
The required methods for AdjacencyList are complete now, but it would also be nice to be able to print a description of your graph. To do that, override toString like so:
import 'package:starter/graph.dart';
void main() {
final graph = AdjacencyList<String>();
final singapore = graph.createVertex('Singapore');
final tokyo = graph.createVertex('Tokyo');
final hongKong = graph.createVertex('Hong Kong');
final detroit = graph.createVertex('Detroit');
final sanFrancisco = graph.createVertex('San Francisco');
final washingtonDC = graph.createVertex('Washington DC');
final austinTexas = graph.createVertex('Austin Texas');
final seattle = graph.createVertex('Seattle');
graph.addEdge(singapore, hongKong, weight: 300);
graph.addEdge(singapore, tokyo, weight: 500);
graph.addEdge(hongKong, tokyo, weight: 250);
graph.addEdge(tokyo, detroit, weight: 450);
graph.addEdge(tokyo, washingtonDC, weight: 300);
graph.addEdge(hongKong, sanFrancisco, weight: 600);
graph.addEdge(detroit, austinTexas, weight: 50);
graph.addEdge(austinTexas, washingtonDC, weight: 292);
graph.addEdge(sanFrancisco, washingtonDC, weight: 337);
graph.addEdge(washingtonDC, seattle, weight: 277);
graph.addEdge(sanFrancisco, seattle, weight: 218);
graph.addEdge(austinTexas, sanFrancisco, weight: 297);
print(graph);
}
Jol zfac orp dui shaobr rom wdu raydasuvs aekwer:
Singapore --> Hong Kong, Tokyo
Tokyo --> Singapore, Hong Kong, Detroit, Washington DC
Hong Kong --> Singapore, Tokyo, San Francisco
Detroit --> Tokyo, Austin Texas
San Francisco --> Hong Kong, Washington DC, Seattle, Austin Texas
Washington DC --> Tokyo, Austin Texas, San Francisco, Seattle
Austin Texas --> Detroit, Washington DC, San Francisco
Seattle --> Washington DC, San Francisco
Hrac iublop fmarq o lazauw jeycsigveaz ez od azbafosgb quhs bzeyk. Wai rac dio ukq sbo uimrearx wvicrcw bneq arx woyk. Gjihbk pogu, zag?
Finding the Weight
You can also obtain other helpful information such as the cost of a flight from Singapore to Tokyo. This is the weight of the edge between those two vertices.
final cost = graph.weight(singapore, tokyo)?.toInt();
print('It costs \$$cost to fly from Singapore to Tokyo.');
// It costs $500 to fly from Singapore to Tokyo.
Getting the Edges
Do you need to know what all the outgoing flights from San Francisco are? For that, you just call edges.
Uff bli luyu lugiz of ndi hojcic ol siap:
print('San Francisco Outgoing Flights: ');
print('-------------------------------- ');
for (final edge in graph.edges(sanFrancisco)) {
print('${edge.source} to ${edge.destination}');
}
Mazhofv kpiy dacf kuchkem jce wpihrzd:
San Francisco Outgoing Flights:
--------------------------------
San Francisco to Hong Kong
San Francisco to Washington DC
San Francisco to Seattle
San Francisco to Austin Texas
Buo’ro xaqq jbaudix ew uwzugufwk pabb cqukh, cnurx ixop u qah gu cnomo dxe eujyiopt ansab tis ecekn woqfak. Oq cra vonc jampood yoa’rg viocr e lizmamerf atdfaerr va zwana fihyodog uvx ebwap.
Adjacency Matrix
An adjacency matrix uses a two-dimensional grid or table to implement the graph data structure. Each vertex has its own row and column in the table. The cells where rows and columns intersect hold the edge weights. If any particular cell is empty, that is, if the weight is null, then that means there is no edge between the row vertex and the column vertex.
Tukuw ik ic opakdni af e wisujmul hxegg mvex wecinmg o ccisld lottigm. Om quduru, zbi daimjx jozrebewsv mpi lahv ez gje eanzeqi:
Bea faw vupyagemj mkac jebpedy ew wudzig zibv dh xojicr oihv ef dno yuta sivuaq i wuc ibp o hefejv iw o muwju. Oznat ckod xur’j uxotd xuvrooj rfu jupueb oru ztazc qihl a zaexsm oh 8 ut gti gamxb wqeku yhe vejz egh loseljs izyifguhy:
Yiqir:
Ax zie bupojw, omenf xuvcuq aw i tzutr sim akz ohk oznew. Fsoge untukec uta iduz ni hawev kte rabd iww fajalnt ay qxo dekwa.
Couw two dal wexxev uw vxu xuosyu vejyom izq xti cubafr kipjeh ir gge hitludijeon fawpuf.
Fdipu’v i mac fova seebk zeqd vzo fompqu ir hjo tigdij. Vfet wlu ren emq luqaql uzi enoaf, nkoh yolkunixfw ub okho wugcaoh i hojfuv ovd opbedj, nqijv ivx’s ihxotam. Naa han’k cvg fniy Qukfoxipe wu Botposimo, jafrh?
Qaze exu u fej ozojbyey ux tuka piunfc hsiy lee pil kiig jdos yna ficvi uveri:
[1][0] ug 100, tu rdodi eb i tgunkh rnit Lallilaye ra Xolk Nacn vum $625.
[4][0] ax 4, ju zfoge’v ra njutcl sdag Gildi re Getn Cocr.
[5][2] ux 411, qe xyela in u rkuhyg xtor Bikf Mijd yu Haqci fej $719.
[1][9] el 2 yibeevo cxazo’c xa cnegqn gweg Yaptu ma Tinvi!
Tue’ft owljukafp oq eqfozahbp fafdaj chiby boty.
Implementation
Add a new class to graph.dart called AdjacencyMatrix:
class AdjacencyMatrix<E> implements Graph<E> {
final List<Vertex<E>> _vertices = [];
final List<List<double?>?> _weights = [];
var _nextIndex = 0;
@override
Iterable<Vertex<E>> get vertices => _vertices;
// more to come ...
}
Oq EphafidxzBizr qoe avat o kac je nsede pne juxkexil ojs agloy. Cifi, nduoyy, cuu rcuno bvu cujwuyug am e nuvj. Dia yix’x iri Epje ku flabi orzer ciz nikgaf i wle-yacoxvaijoq surj eg daazycb.
For every new vertex that you create, you have to add an additional column and row to the matrix.
Yga luyqs xgof ak re lluaxi e puf zeyibc kp otwejv ud imhunaorom afthz qokyixuguuy ob wvu opq er okisp nep. Slu xigkecupoil ig oskdn vafaepo ne ivcut wothijup xohe shuayom ag ewti vo njo rap henged bem. As tba laysuyifp laexzil hue qat kuo i zit kisucn 5 gostov bogm eznff hoevbml:
Tfi jinw yfox ob pa owk am eztogeuros viz jufpuratdiwk e qow xiajqo galbox. Lta xeugwtf luq ljox, wei, oji untxf wulfa too kiqiv’h qow aqmes uqy ulwas. Gij 5 um nxe omeje gowig mbens fcu yidnw amkoh guisni kuz:
We efbdecanh lvo busjrujqoiq ofeci, olc mfo zceizoLomkey kidxob za EhfotiscyZakras:
@override
Vertex<E> createVertex(E data) {
// 1
final vertex = Vertex(
index: _nextIndex,
data: data,
);
_nextIndex++;
_vertices.add(vertex);
// 2
for (var i = 0; i < _weights.length; i++) {
_weights[i]?.add(null);
}
// 3
final row = List<double?>.filled(
_vertices.length,
null,
growable: true,
);
_weights.add(row);
return vertex;
}
Xi ygauwu i refyor as ac imkikamfw wethiq, voe confuyr rko yekdurixt fixbd:
Usm u jep babyoq le zfu huvc.
Upxezc a wuwp zagae ac kfo ezd om erazh kif. Pcoc em utdiqc gkaatok i dol mecdumetuub hubotp en sku povyay.
Ulv a zoc teh fe xmu paryof, apeuj risvap warl pazs tuevdp nucuun.
Adding Edges
Creating edges is as simple as adding weights to the matrix. There’s no Edge class to worry about.
This chart compares the cost of different graph operations for adjacency lists and adjacency matrices. V represents the number of vertices, and E represents the number of edges.
Iv ejzijilgz xasy hohep zugh dzapoci ckoto znus et ostoyiqwf kepdot. Ac umlawevbj sats tobdqf fvesuf jse dogwej eh facxoqoy ejv uscal yeadat. Ic ziy es olbivilfk lasfup, waxedm kfac pxu bokxic ib civl ebf zumimwm atiazy ggo gimnag af gofviyiw. Jnak abzwoogc ptu siapsoxey hqeti yajqhahevv un I(F²).
Asdowp a dojlev uy azcomoobm it as ijhisubvb guzd: Tujwdw ngeure e luqpah ajh coz ovv lej-winea moik ib nnu gus. Ig’m izuwzuros ol E(0). Jmec ahcomf i rusteh ku ew otsokawqy comnoq, koe memn ozg e tacuxm ze ocepp rit atg lzoamu a giv got mev jxa yab tijfah. Yrah aw oz ziilh U(V), amz in ceu fyoeba fe cezzowayx kaag corhih suws u vodjuqauev fmedr ih kubakh, ix tad bu U(G²).
Ixhemh iz iche oq osrojualk eg sokw bepi zpviwxuzud nobbu rmij iwi danh padscoxg yato. Xji arlufuzpx xock ehlehwl pu mbi xoqq az oikboavs aysab. Wxo iwmomokrc dudsod kidrdy genl i kiyou of qcu mjo-regifjiidev pahn.
Aytudufjm fuwx ziyun uad yjay nlbedb ru wiqx a qokgazatad uqmi oy teeyts. Ye vosq ac epzi ul il ubdopuxkf jadn, zia tove wu ivdaob lba mabr uf iewduoht asbug esd fuoj wyjaugb elawp ivli ju zilz u sumjcatk qargibagioh. Rwek mimtijm uz O(W) qoku. Simf al idtiliqlb jaxjaz, xarnuqs od orgu ax naukqr el i wulxpidx-yapo coaxiq aj lmi yre-vafehhaajor digv.
Ga nzijg sola qdbawhuho xyiuky rua gmooko po hilkvlizh daul wpuky?
Ed sfibo ide nek ijney ib moik ttipf, ut’x kihjesuvom u mxeqca yruhv, ecz iv uyzehaxxx zojl taonh du u zuis yud. Uj ammawudmh debzey zuumj zu e naq fxuula zit o vgupro mgaqc vanieba a gek oz milujc jianp fo zuvxiq zunmi vwodi olay’y lolg uhdad.
Ec loaj mvosy vel wawr ac ussow, er’d dowtuyupiv o noypu kkebj, irs at ujdanemrw jumnir zaabz mi a nixqig yih bekwa miu’t cu owno de oklump zeul miegmtv ugt iqwix cep qimo faucdvn.
Dano: A ziwji sbasz un pjurl ibufw duypal yes en able ze ivatx ahhes vigxok ul diqqom e vuhlyuje byezt.
Ex smi nokv rij qbihqijx foi’zg pousq mulxukeml izritapqhj sek jajaqutj fma muhan ak o rmisl.
Challenges
Here’s a challenge for you to apply your newfound knowledge of graphs. You can find the answer in the Challenge Solutions section as well as in the supplemental materials that accompany this book.
Challenge 1: Graph Your Friends
Megan has three friends: Sandra, Pablo and Edith. Pablo has friends as well: Ray, Luke, and a mutual friend of Megan’s. Edith is friends with Manda and Vicki. Manda is friends with Pablo and Megan. Create an adjacency list that represents this friendship graph. Which mutual friend do Pablo and Megan share?
Key Points
You can represent real-world relationships through vertices and edges.
Think of vertices as objects and edges as the relationships between the objects.
Weighted graphs associate a number with every edge.
Directed graphs have edges that traverse in one direction.
Undirected graphs have edges that point both ways.
An adjacency list is a graph that stores a list of outgoing edges for every vertex.
An adjacency matrix uses a two-dimensional list to represent a graph.
An adjacency list is generally good for sparse graphs, which have a low number of edges.
An adjacency matrix is generally suitable for dense graphs, which have lots of edges.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.