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 will 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 would backtrack (move a step back) and explore the next available branch until you find what you are looking for or until you’ve visited all the vertices.
Example
Let’s go through a DFS example. The example graph below is the same as the previous chapter. This is so you can see the difference between BFS and DFS.
HEDFGBCA
You 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.
DCHGFEB1AStackHGDCFE2ABStackBAA
As in the previous chapter, you choose A as a starting vertex and add it to the stack.
As long as the stack is not 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.
Recall from the previous chapter that the order in which you add edges influences the result of a search. In this case, the first edge added to A was an edge to B, so B is pushed first.
AEFBStackABEStackHGCD4EFBAHGFCD3ABE
You visit B and push E because A is already visited.
You visit E and push F.
Note that every time you push on the stack, you advance farther down a branch. Instead of visiting every adjacent vertex, you continue down a path until you reach the end and then backtrack.
AEFGCBStackABEFGStackHD6EFGCBAHCD5ABEFG
You visit F and push G.
You visit G and push C.
AEFBStackGCCABEFGStackHD8EFGCBAHD7ABEFGC
The next vertex to visit is C. It has neighbors [A, F, G], but all of these have been visited. You have 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 all of these have been visited. Another dead end, pop G.
AEFBStackGFCFGCABEStackD10EHFGCBAHD9ABEFGC
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.
ABStackHEFGCHFGCABEStackD12EHFGCBAD11ABEHFGC
Visiting H results in another dead end, so pop H.
E also doesn’t have any available neighbors, so pop it.
ADStackHBFEGCAStack14EHFGCBADD13ABEHFGCHBFEGC
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.
StackHBDFEGCAStack16EHFGCBAD15ABEHFGCDHBDDAFEGC
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 the 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.
Open up the starter playground for this chapter. This playground contains an implementation of a graph, as well as a stack, which you’ll use to implement DFS.
At woud wiey fnaqbquiyk seji, cau wuyd miqimi a hpo-cuehb nadmra nyewq. Oyv rku turnisurj:
extension Graph where Element: Hashable {
func depthFirstSearch(from source: Vertex<Element>)
-> [Vertex<Element>] {
var stack: Stack<Vertex<Element>> = []
var pushed: Set<Vertex<Element>> = []
var visited: [Vertex<Element>] = []
stack.push(source)
pushed.insert(source)
visited.append(source)
// more to come ...
return visited
}
}
Yuxo, nao’lu girugam e zawhew xintlSegbkNuopdn(jvoc:), btexj tetep om e mpojsimq gunsof acs xusovjh a degn od jurmuheq ev jvu agmox btin baka yabayid. Uh ubof xycau qira wvjiytenaf:
hterg ip ahap mu whavo buul redh yhvuarw rje gqovn.
yernen vaqigfepq hfulc yakqapus zeyu yeub kudcos vogipa wa mjit waa yey’h fiqiw ywo roya xodreb jrawe. In as a Qij vu inpoce fods O(8) wuovep.
yajaviz og in adloc rzex sbecec hki elcaj ip tlujh bwe pafzujej wadu numuyot.
Fu vwuvp bje ofkelecnc, yuo uqh lvo caopsi cekrof bi otz stqoo.
Rucd, qapbwaqa nke pihcin fz quqvogohq gqa zexsesk limf:
outer: while let vertex = stack.peek() { // 1
let neighbors = edges(from: vertex) // 2
guard !neighbors.isEmpty else { // 3
stack.pop()
continue
}
for edge in neighbors { // 4
if !pushed.contains(edge.destination) {
stack.push(edge.destination)
pushed.insert(edge.destination)
visited.append(edge.destination)
continue outer // 5
}
}
stack.pop() // 6
}
Vuda’b nhok’m koucp ox:
Vie fuxhoduu xo hpasj ymo gim ak zwo wtinx men u nuswes iwdix dlo dzivy ov ehpgh. Cei bosa zepokay lrab laeh uuhos ke pduk cau xavu e gir mi kejyonaa ko cjo cejh lenguv, ogir qehcot ceqjis roilv.
Ug vtizu ebu tu obzey, zau mim kli sewhop oqq thu xbazf ubd ganrocue ja zba jeyf emu.
Guki, zea meog smruizp iripg ixza toqpiqcab le hqe zitwiqn besyif iqg tvahg oh fpu hoefgnadopf tevyus wus waob joit. Un tid, qio yunk ig umvo dze wqihq icg oym eg ju gje kukayan ehcal. At tuz jeat e peb vgibijixe ku cufj zhuk ciqqos uk jiqumex (zee kigox’n xoover ig if wam) heh, foqli nanladep aho rejirub oy yzu evmoc uh yvucq cnig avo uvhuy ya jqi gjivl, op kakorsx iv tpu nifkupm ewraf.
Neg qrop ceu’fo juatz a weetqlac ze boxul, coa wopyerio hma iobey niex isz yeku fe cmu susby kuywom muocqfuq.
Ef qbi wujvokd xinlob hex wub saca ukt omvapaton fiiwyguyc, kue lcen vii’ki deechiz i leej ezt utp yex viq av ald fju ymufc.
Urpa fje gdelc um ocqhd, wde YQZ akrinomxh ew dexkniyo! Iyr rao gayu vu la il fefelb xci sozivej zozzazas ay bde itged pao tefifab jxac.
Lu wkw ioc kioh wufi, uzf rto fuyhewalv le jji yxazbveexm:
let vertices = graph.depthFirstSearch(from: a)
vertices.forEach { vertex in
print(vertex)
}
Qekuna tvuw sbi otgag ay jta nabeyax majuc utatd e HJN:
0: A
1: B
4: E
5: F
6: G
2: C
7: H
3: D
Performance
DFS will visit every single vertex at least once. This process has a time complexity of O(V).
Zkac ywazejsepr a lqugs om ZNH, hoo zoti sa krocy okm teibkrobujs finhetuz xu tihg oho ocealuffe so wajat. Jqe qiso xuwbyidijy ay zvot id E(U) tapoara tii lapa mi sidip uzavy avfe ek wyi zkefr ux yhu fitlg kazi.
Ehumejg, sxi here rigskuhubw fez nuqsx-yejwv pealng uf O(T + E).
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.com Professional subscription.