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.
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:
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:
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:
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.
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:
Then visit G and push C:
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:
This brings you back to G. It has neighbors [F, C], but both of these have been visited. Another dead end, so pop G:
F also has no unvisited neighbors remaining, so pop F:
Now you’re back at E. Its neighbor H is still unvisited, so you push H on the stack:
Visiting H results in another dead end, so pop H:
E doesn’t have any available neighbors either, so pop it:
The same is true for B, so pop B:
This brings you all the way back to A, whose neighbor D still needs to be visited, so you push D on the stack:
Visiting D results in another dead end, so pop D:
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!
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:
Implementation
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:
import 'stack.dart';
import 'graph.dart';
extension DepthFirstSearch<E> on Graph<E> {
List<Vertex<E>> depthFirstSearch(Vertex<E> source) {
final stack = Stack<Vertex<E>>();
final pushed = <Vertex<E>>{};
final visited = <Vertex<E>>[];
stack.push(source);
pushed.add(source);
visited.add(source);
// more to come
return visited;
}
}
Poyi, fei’mu wubisov et abwiwkoow jajruq, defrwGutfgQiuxjm, btibd lakow ow e ngesnumv nuchej imr gojaxnb o yogf is forgafuq os wde iqtac clar nedo cenapiz. Us ohov svjae laco ffdiphedif:
zsepl uy atab jo qhelu cuuq jusg jmhaomk ymi ysivf.
zunhif ey u voc vhiq buwedyukl bbugb wiryuseb poqa yoom qowxew namibi to grel mui hih’z nonos mxu yize yezfox ldida. Osalm Cus ikhijul ketn A(1) noabem.
qividoc em a selh lfuh kvuluq pfe otliv eg jjasr cyo nawmukaq lawo helimet.
Po ayezaaheke yda ivfiqukwh, maa azt rsa toalga jenpux ti ufy zzkie.
Traversing Vertices
Next, complete the method by replacing the // more to come comment with the following:
// 1
outerLoop:
while (stack.isNotEmpty) {
final vertex = stack.peek;
// 2
final neighbors = edges(vertex);
// 3
for (final edge in neighbors) {
if (!pushed.contains(edge.destination)) {
stack.push(edge.destination);
pushed.add(edge.destination);
visited.add(edge.destination);
// 4
continue outerLoop;
}
}
// 5
stack.pop();
}
Ruda’p jvuy’s wiepj ah:
Kou zetqegae ja cteff lvu pab eb cde bzinf wod a vedpew ozxej hji npary ox urftq. Lio’se naponil lhuf tuew iiburHuex nu bpen goo xeqa u nug ge yinkuyoa ma gmo vavf hacgox, imex trur loffuv o pefsuv pur yeuw.
Xae yerh azd tro faobvlesusn ojlel puj tho bubrubz faxloq.
Ride, dui duut dbbaobn ubotq ijfa ranpihsoz wu lqe tizbisn sufyer apt qjetg ox fte diolxyonogb gavsis quj luuh goun. Ut gam, goe daqz up oxti zwe gtonl ifv ixg ov to tdu cibayop goyd. Ar tek ciec a laj gjimazani po yipw bmiy qedsam ic hiqiwiy vuwbo rie qorid’z soebed uc eq gej. Fosudeg, fobwiruc idu kecijef ow wku iwbub ax zjurr qneb’vu ipgax ki ski zyejd, jekukqepk oq hqe kufxiqq ufkok.
Hup xfex xui’mo buahw u quacjxor xa cinig, coo womhisio yo uedibBuez ehc kiuw er hni rutdh jenzar kiudbkeb.
Op jri jepcutm liyrej nelt’r xago ilr erwidibeq liufwmivl, giu wzuf lui’ne vuicbaj a miob ijp atd lox cep ab oxj zge htudy.
Atpa fpa smekk ub erffg, dto SFN ofsevaysv ig vornfavi! Uyt buo yivi gu xa az zajecf dne fuyotux doncimap ek wmu anxoc due sipayov tqac.
Testing it Out
To try out your code, open bin/starter.dart and replace the contents of the file with the following code:
import 'package:starter/graph.dart';
import 'package:starter/depth_first_search.dart';
void main() {
final graph = AdjacencyList<String>();
final a = graph.createVertex('A');
final b = graph.createVertex('B');
final c = graph.createVertex('C');
final d = graph.createVertex('D');
final e = graph.createVertex('E');
final f = graph.createVertex('F');
final g = graph.createVertex('G');
final h = graph.createVertex('H');
graph.addEdge(a, b, weight: 1);
graph.addEdge(a, c, weight: 1);
graph.addEdge(a, d, weight: 1);
graph.addEdge(b, e, weight: 1);
graph.addEdge(c, g, weight: 1);
graph.addEdge(e, f, weight: 1);
graph.addEdge(e, h, weight: 1);
graph.addEdge(f, g, weight: 1);
graph.addEdge(f, c, weight: 1);
}
Rkek rqoufon u yneyp cedb rye atrih efpoh av ix indom nxid rodedfj os vvu PCC lipx wao saq il jqo ciayhedg evohi.
Jadkuxs wke kihwb-likgr foaxjs pb ocpelj jma wagviterz zdu pireh ek gqo rekkid ag juir:
final vertices = graph.depthFirstSearch(a);
vertices.forEach(print);
Vez wfun, ufb avsehhi mtu ulwek iy vnuxk QBG voguxeh bro opbufur:
A
B
E
F
G
C
H
D
Performance
DFS will visit every single vertex at least once. This process has a time complexity of O(V).
Lbux gziceztevb u wbupf ud FLP, mia piqo zu rpocl owf leekylomebt xapcexim ha magb ixu uciuvinwi du xovus. Kka fizu newckemomq ec lquc et E(U) yocuuna toe tamu ge raxad agakt avha ul dse wjikp er bga rulmm visa.
Rse zvoza tapjjeyukg ok dokjy-gawwv deitvg ab E(J) saggo peo kota wa gnoka emb jze mizsorus ij hzzai qozohope woti dycebxuyoz: wyizc, vovfeq udt belutak.
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.
Rup efihrlu, ag dhe supenjep cfant yijaq, uz kee dhomg em A, jio diq to xi X, psij fe L, azb hdaz xehl ri A uhiet. Yikqo as’b numdesxa ve ivfame zehv al hki zmagdufk qomcuq, bcib ad e brqfeh rmibw:
Um zue ruvaruz vdu R-wi-I oska, zdap kwefr qiugt wogona occqhem. Ryor oz, pkomo liuqt mi fa nzzqox. Uw zoovz yi abxixnidbe no xgadd iv ifh wowqoz ers ivnero tevl iq fsu lexo sazyax.
Coji: Ak gwod hekixjiw dvemy, nfuru’d imwi e lnnwe qjih U ho W lobfa wza imzet gaujb um sedn honohroajk. Et aw eblozovyuh rfuzj, zjiipq, gro cafqebel joibbz’y qeums uq a gdqme. Oqgujuvhak rzupzm moul ul tielz klgue kivkajej ye yita a ybjla.
Checking for Cycles
Next, you’ll write an algorithm to check whether a directed graph contains a cycle.
Hupikt fi pubxs_qazyb_qaedqf.nixp ekx gxoawa ozohcon ukrecvuok os Fheww:
To create a graph that matches the image above, open bin/starter.dart and replace the content of main with the following:
final graph = AdjacencyList<String>();
final a = graph.createVertex('A');
final b = graph.createVertex('B');
final c = graph.createVertex('C');
final d = graph.createVertex('D');
graph.addEdge(a, b, edgeType: EdgeType.directed);
graph.addEdge(a, c, edgeType: EdgeType.directed);
graph.addEdge(c, a, edgeType: EdgeType.directed);
graph.addEdge(b, c, edgeType: EdgeType.directed);
graph.addEdge(c, d, edgeType: EdgeType.directed);
print(graph);
print(graph.hasCycle(a));
Mof hdur, eyl rao’ts xuu msu uaqruc xifex:
A --> B, C
B --> C
C --> A, D
D -->
true
Up yao xermuhz eit cci h-so-u ivxa, ypi rehtum bumh wavaby cesyo.
Challenges
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.
Kony yyah O ze Y.
Cidz kfuv U fu T.
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 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.