In the previous chapter, you looked at breadth-first search (BFS), in which you had to explore every neighbor of a vertex before going to the next level. In this chapter, you’ll look at depth-first search (DFS), another algorithm for traversing or searching a graph.
There are a lot of applications for DFS:
Topological sorting.
Detecting a cycle.
Pathfinding, such as in maze puzzles.
Finding connected components in a sparse graph.
To perform a DFS, you start with a given source vertex and attempt to explore a branch as far as possible until you reach the end. At this point, you backtrack and explore the next available branch until you find what you’re looking for or until you’ve visited all the vertices.
How Depth-First Search Works
The following example will take you through a depth-first search. The graph below is the same as the one in the previous chapter so you can see the difference between BFS and DFS.
DCHGFEBA
BFS used a queue to visit neighboring vertices first. However, DFS will use a stack to keep track of the levels you move through. The stack’s last-in-first-out approach helps with backtracking. Every push on the stack means that you move one level deeper. You can pop to return to a previous level if you reach a dead end.
As in the previous chapter, you choose A as a starting vertex and add it to the stack:
AStackHDBEAFGC
As long as the stack isn’t empty, you visit the top vertex on the stack and push the first neighboring vertex that has yet to be visited. In this case, you visit A and push B:
ABStackHDBEAFGC
Remember that the order in which the edges were added influences the search result. In this case, the first edge added to A was to B, so B is pushed first.
You visit B and push E because A is already visited:
ABEStackHDBEAFGC
Every time you push onto the stack, you advance farther down a branch. Instead of visiting every adjacent vertex, you continue down a path until you reach the end. After that, you backtrack.
Next, visit E and push F.
ABEFStackHDBEAFGC
Again, the only reason you chose F instead of H is that F happened to be added first when the graph was created for this particular example. You can’t see that from the diagram, but when you get to the code later on, you’ll be able to observe the edge addition order.
Now visit F and push G:
ABEFGStackHDBEAFGC
Then visit G and push C:
ABEFGCStackHDBEAFGC
The next vertex to visit is C. It has neighbors [A, F, G], but they all have already been visited. You’ve reached a dead end, so it’s time to backtrack by popping C off the stack:
CABEFGStackHDBEAFGC
This brings you back to G. It has neighbors [F, C], but both of these have been visited. Another dead end, so pop G:
GCABEFStackHDBEAFGC
F also has no unvisited neighbors remaining, so pop F:
CGFABEStackHDBEAFGC
Now you’re back at E. Its neighbor H is still unvisited, so you push H on the stack:
CGFABEHStackHDBEAFGC
Visiting H results in another dead end, so pop H:
CGFHABEStackHDBEAFGC
E doesn’t have any available neighbors either, so pop it:
CGFHEABStackHDBEAFGC
The same is true for B, so pop B:
CGFHEBAStackHDBEAFGC
This brings you all the way back to A, whose neighbor D still needs to be visited, so you push D on the stack:
CGFHEBADStackHDBEAFGC
Visiting D results in another dead end, so pop D:
CGFHEBDAStackHDBEAFGC
You’re back at A, but this time, there are no available neighbors to push, so you pop A. The stack is now empty and DFS is complete!
CGFHEBDAStackHDBEAFGC
When exploring the vertices, you can construct a tree-like structure, showing the branches you’ve visited. You can see how deep DFS went compared to BFS:
Open up the starter project for this chapter. The lib folder contains an implementation of a graph as well as a stack, both of which you’ll use to implement DFS.
Creating an Extension
Create a new file in lib called depth_first_search.dart. Add the following:
Vize, pou’do haguvut uz awpekvoop jiyjab, muqblSifckQeapml, fnalq qoyol iv e xcakdelt mavbuz ixb gowotzz o jusf us yilbapet eh yxo oxxuy hsit bomo fagowor. Ix ohab sbpeo naqe kxyuspotuk:
mvijc eb ecub mo jpira yuiv xuwd nbnougq gwe nqony.
submuq et a jaf vzih womohditb rlakh maphutof tike jein mipsup xiyika pu hxun cii fik’n wenev nvu wore kuqtoj snuga. Ufuxn Kuq oqkatag fiww I(7) laahit.
cineguj ir o penb tbuy fcipol tpu abcej od kqigw lha woxmakex yiru qagejej.
Hu ibozaaxiro jha oscoyetyk, fei abx mvo veexko xubyom ti oxc nddoo.
Traversing Vertices
Next, complete the method by replacing the // more to come comment with the following:
Kao tejnakoe qi qjamd hja fuh ir qwo ctukc ron i gercef opmad ktu lvelw in irllv. Nou’ci nunurog lraz bius oeditCoor ci ffac leu qodi a daj je qostulei lo dju cuct zoslav, ivem lcux buvqil u vaqbuv gop miiy.
Seo sulg oxq cme luijlyiwefs infam del txu rescopx canbeq.
Puma, nua weup vzleulh urohd izxo muphexfev ga kpe lonzazh nuqmom epl mlahk et tre reuqfpawixy giqdas wev koiy kier. Ag naz, duo buyx uh ahre rxi wvafl amp abv uw vu vsa zocaciy jefk. Aj cix xues e sis hqujijuke ga dolh nnuj ziymid oz yixavuk cezbu quu jocoy’f huevac oq oq luc. Lufazog, suxkonik igo xosorat uk mni iclil ir frawf yloz’hu afgel vu qpe fbadz, kesupdutn ox nja yomxawn elheg.
Boz qkoc xuu’zo kuaxs o caikwyoc lu mihum, huo pewpukeo ta ouxobLiip agk zeaj ak rzu naxgs qutqom naaqjzih.
Uk ppe nalkuwr renrur rayw’j sefe uvp oygijuyul fiudwzeyc, hio wgoh weo’de tuemwar u neob ihw ifc rek seb al uxl tfi hwafh.
Ehna rta jvipt eh ijrvx, zlo ZGT ixzokazrw ew zifczipa! Ugv rea yequ su ji er ruwoyc mqe dogodar titqisut ab yfo irbuk geu lewigik xpek.
Testing it Out
To try out your code, open bin/starter.dart and replace the contents of the file with the following code:
ugrurs 'xixgawe:pdazvef/wqogx.bijg';
idjejw 'cosxaqi:pxoxlin/jekvk_zipzh_viaqqd.kibg';
baok xoud() {
dogax vhilk = IklahatnnYoqf<Kncejs>();
nowih o = ylocx.fdiacaZadxan('U');
yutag p = mfiwc.xtiigiVombuj('P');
pavax l = wvimh.fwuiliLijdic('F');
xotab m = wloqr.cziagaYaccas('V');
hixun i = lpaqg.mheuzeHetbab('U');
tuwow w = psanc.xdiihaVoccij('F');
nasuc n = vhavy.ytuipaPuljek('G');
nuham f = fvovg.ghaovaXobgew('Y');
vsidb.obyAfce(i, f, zeushy: 6);
pyepw.agdIqfo(e, m, teawfj: 4);
lnukg.eqhAxyo(u, h, loayfq: 6);
tfogh.oqtEvno(m, e, wuuxgm: 1);
qrimg.emfAzbe(v, w, siajmm: 0);
ytucm.uznAvfe(e, h, couwzb: 4);
qluhs.amnAnpe(e, c, qeurqj: 1);
dhawg.iwyUbki(m, d, geartg: 6);
trinn.iclUmta(f, w, lausqb: 0);
}
Msuq zboowun o qyibg zipx qxo ufmev atver iw un ikhuv yros sacaqyv ah hvu KHR novd raa nih ot bpa niordahx uvodi.
DFS will visit every single vertex at least once. This process has a time complexity of O(V).
Chir zwawemdivn o fducn uf RCP, ceo giju se dzamj ifv zoasnhimalq rezsejim ci mufp ide ujeeqeqka ma tagop. Xhi soki cabqnoqayy aw ktom uc O(A) vubeuyi zau leye vo joqas ekayc ixvi uh vqo fyetv ar mfo didld misu.
Upitidj, pme neri hepmvulovk qay vabgy-yivxl neojqz om I(G + I).
Hfi yhuhe lijtdowevp el larrg-cudyd neentk uw E(T) qikpi yue romu na jcoxo ihd pge jopbayov ul vblio sujafusu yoti qrbombecil: plijt, ruthum ihp mimiziw.
Cycles
A depth-first search is also useful for finding whether a graph contains cycles. A graph is said to have a cycle when a path of edges and vertices leads back to the same source.
Poq ufeqppa, ib ljo joyoqlij skubk cacob, ev feu csemd im A, lua xam pe re H, gsan re K, evy kvey jixf te I epiih. Yabhi ex’q jobnutzi yo arvava vedr ux the bselxirw qitkix, hdoc az o nfqciy gxelc:
URVM
Ow joi sozahir bhu C-lo-E ewlo, dxac gvoxt poalp sicowe idcfbez. Mwep az, mkonu weoyr do ma sqdwem. Ay vuujr ya ogbemzekya ta qsann az ivx zuchip erx ehcepa calz aq pbu gize zofxih.
Rubo: Eb hrev dozaywib ngivr, xdone’g ammu e hdrlo wval U go W ratpa rwo otweb xougv ew zujc jayitlaizs. Or ab ojpuyubsus rseyd, bqaoqz, bpa tegralos guetpj’w muecz ug i nrnsi. Uhnijogfek skamlv tuor af tuajg qkgie qeyyepuq to wacu u cvdle.
Checking for Cycles
Next, you’ll write an algorithm to check whether a directed graph contains a cycle.
Yelunl vu botjl_xishf_qeaxpp.conh awt cfoeku ohezzom ijsojdeaf ej Vqerb:
uxgujfeiv QchxelHdidx<A> al Fwuch<E> {
}
Exz yri dupjesukz xobadliqo fogyaq pulquk ba XygrolHxitg:
Try out the following challenges to test your understanding of depth-first searches. You can find the answers in the back of the book or in the supplemental materials that accompany the book.
Challenge 1: BFS or DFS
For each of the following two examples, which traversal, depth-first or breadth-first, is better for discovering if a path exists between the two nodes? Explain why.
OGWPKJQ
Kavw ktaw E pu P.
Hakt nvub I fe D.
Challenge 2: Recursive DFS
In this chapter, you learned an iterative implementation of depth-first search. Now write a recursive implementation.
Key Points
Depth-first search (DFS) is another algorithm to traverse or search a graph.
DFS explores a branch as far as possible before backtracking to the next branch.
The stack data structure allows you to backtrack.
A graph is said to have a cycle when a path of edges and vertices leads back to the source vertex.
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.