Depth-First Search
DFS is a graph search algorithm whose frontier is kept on a FIFO queue to ensure that the deepest nodes are all explored first which comes with the guarantee that given enough time BFS always find the solution if it exists. DFS returns the first solution it finds with no guarantee of optimality. Time complexity is a function of the size of the branching factor & depth of the search graph, i.e. O(b^d), but space complexity is an improvement over BFS, only requires O(bm) where m is the max depth of the search tree.
Algorithm
DFS is the same as Breadth-First Search β― Breadth-First Search except that it utilizes a FIFO queue for the frontier which leads to gaining depth first instead of the shallow exploration of BFS. See BFS by the link above for the algorithm or see below for a simple python implementation.
There is also a commonly useful recursive implementation of depth first search.
problems
- pacman grid world
- universal sink
- is graph a valid tree?
- binary tree maximum path sum
- binary tree inorder traversal
- binary tree postorder traversal
- Minimum Fuel Cost to Report to the Capital
- Find the source nodes.
- Topological Sort
- Strongly Connected Components
- Find the sink nodes. Β
PacMan Grid World
Given a grid of dimension R x C, the locations of PacMan, the food, & the string based grid world; implement a function that performs DFS, printing out the number of nodes expanded on the first line followed by each node visited βR Cβ on each following line. Include the initial problem state. The expansion of a node should order its children UP, LEFT, RIGHT, DOWN where they exist.
Explore
The instruction is explicit, weβre implementing DFS. The following tree has nodes numbered by the order they would be explored by the DFS algorithm.
βββββ DFS runs on graphs other than trees. One
β 1 β must keep track of which nodes have been
βββ¬ββ explored & avoid them. E.g.
ββββββββΌβββββββ
βΌ βΌ βΌ ββββββββ
βββββ βββββ βββββ β βΌ
β 2 β β 4 β β 5 β βββ΄ββ βββββ βββββ
βββ¬ββ βββββ βββ¬ββ β 2 ββββ€ 1 βββΊβ 4 β
β ββββ΄βββ βββ¬ββ βββββ βββββ
βΌ βΌ βΌ βΌ
βββββ βββββ βββββ βββββ
β 3 β β 6 β β 7 β β 3 β
βββββ βββ¬ββ βββββ βββββ
βΌ
βββββ In other search algorithms, the shortest
β 8 β path discovered so far must be kept track
βββββ of & compared against rediscoveries.
Python Solution
Complexity Analysis
If each state has at most 3 successor states, then our time complexity of O(3^d) where d is the depth of search tree through the grid. For each node we store a constant amount of information yielding a space complexity of O(3^d). Here, three is known as the branching factor, which as you can imagine becomes quickly unwieldy as depth increases.
C++ Solution
TODO: c++ solution
Β
Universal Sink
Given a list of people & a function that returns whether a knows b, find the person everyone knows & who knows nobody else, i.e. the celebrity, if one exists, else return -1.
Explore
- TODO
Python Solution
Complexity Analysis
This is O(n) time & space because we are ignoring edges the go backwards in the graph. This linearises the graph, so this is a linear search. Β
Is Graph a Valid Tree?
Given a list of edges, check to see if it describes a valid tree.
Explore
The idea is to run DFS on the graph & check to see if any node is seen twice. If so, then the graph is not a valid tree. We are given a list of edges & therefore must construct our graph, weβll use an adjacency list to do so.
Python Solution
Β
Binary Tree Maximum Path Sum
Given the root node of binary tree, determine the maximum path sum between any two nodes.
Explore
-10
ββββββββββββββ
βΌ βΌ
9 20
βββββββββββββ
βΌ βΌ
15 7
-> 42
- Recurse to the deepest node & calculate the mps. The mps on a leaf node is the value of that node. Continue calculating mps as you recurse back up the stack. The mps for a 3 node binary subtree is mps(left) + mps(right) + self. This is analogous to depth first search.
Python Solution
Complexity
We traverse the tree by recursive depth first search in linear time, O(V), using constant, O(1), space to keep track of our maximum path sum.
Β
Binary Tree Inorder Traversal
Given the root of a binary tree, return the node values corresponding to its inorder traversal.
Python Solution
Complexity
DFS is O(v+e) but we have a binary tree which has 2^h nodes at any height & there 2^(h+1) total nodes where h is the highest level. This implies that the height of the tree is logv & there are 2^logv = v nodes which is O(v) time. An easier way to see this is that a binary tree has one edge for every node except the root node β O(v+v-1) β O(v). We use O(logv) space for the recursion where logv is the height of the tree.
Β
Binary Tree Postorder Traversal
Given the root of a binary tree, return the node values corresponding to the postorder traversal.
Explore
5
1 4
2 3
- Postorder traversal of a binary tree is DFS with a left, right, root ordering.
Python Solution
Complexity
DFS on a binary tree is O(logv) time where logv is the height of the tree, & O(logv) space on the stack from the recursion.
Β
Minimum Fuel Cost to Report to the Capital
Given a list of roads, each road a list of 2 ends, integers that correspond to cities, & the number of seats per car, output the minimum fuel cost it takes to get a representative from every city to the capital, city#=0. Each city has a representative, each representative has a car with the given number of seats, each road costs 1 unit of fuel per car to traverse, & representatives may ride together if they end up in the same city.
Explore
2
0 s=2
1 3 4 5
- DFS
- Keep track of the running cost of travel. Calculate the cost at each node by dividing the number of representatives at a city by the number of seats & take the ceiling to find the number of cars that have to be taken, i.e. the fuel cost to go one step closer to the capital.
- Return the list of travelers between cities at each step for use in the calculation at the next city.
Python Solution
Complexity
It takes O(E) time to build the graph & O(V+E) time to traverse it, so the worst case is O(V+E) time & O(V+E) space to store the graph.