You are on page 1of 17

Breadth-First Search

Published Apr 1, 2023•Updated Apr 6, 2023

Contribute to Docs

Breadth-first search (BFS) is a traversing algorithm for unweighted graphs. This is a


foundational algorithm in graph theory from which many other algorithms start.

Features
Some of the features and constraints that define the use and functionality of a breadth-
first search algorithm include the following:

 A relatively simple and efficient algorithm for finding the shortest-path in


graphs that do not have edge weights.
 It has a time complexity of O(|V| + |E|), which is the sum of the vertices and
edges.
 In addition to finding the shortest path between two nodes, this algorithm can
be used to map or traverse a graph to find all connections or if a given path
exists between any pair of nodes.

Implementation
The example below walks through a basic implementation of BFS that will take a
starting node, an end node, and a graph as arguments. However, to start, a brief
overview of the necessary steps or pseudocode will serve as an outline for the final
implementation.

The BFS algorithm is built upon a simple process of elimination. It begins with the
selection of a node and the identification of all the neighboring nodes. The algorithm
iterates through these basic steps until all nodes have been evaluated (or another
objective has been achieved). Through this process, the graph is explored in a layered
approach where each node at a given level (or degree) is evaluated before moving on
to the next. Implicitly, a search employing this process will yield the shortest path
between any two nodes.

As noted in the description above, the BFS process is an iterative one. The
implementation commonly revolves around adding and removing nodes as their
evaluated from a queue.

The implementation below can be broken down into the following steps:

 A list is initialized with the starting node, and nodes will be added to and
removed from this list in a queue-like manner.
 Another list is initialized for tracking nodes that have already been processed.
 A variable is initialized for tracking the path (the sequence of nodes from the
source to the final node).
 A variable is initialized to represent the current node being evaluated (the last
item in the path).
 A loop is set to iterate over items in the list/queue.
 The process starts in earnest by iterating over all edge-pairs to identify the
starting node.
 If the starting node is present in an edge-pair, as the source or destination, then
the accompanying (neighboring) node is added to the neighbors list.
 Once all the neighbors have been collected, another loop iterates over this list.
 A second variable for updating the path (curr_path) is instatiated. This variable is
the sum of the existing steps in addition to the current node.
 Each neighbor is tested. If it matches the destination node, the search is
complete and curr_path is returned.
 If the neighbor is not a match, the path is added to the queue.
 Once all the neighbors have been exhausted, the curr_node is added to the tested
nodes list. Now, the process starts again from the top with the next path in the
queue.
 The loop iterates until the queue is empty or until a path is returned.
 If all the nodes are processed and there is no path, an empty list is returned.
Example
The example below generates a random graph and solves the shortest path between
two nodes. For the sake of succinctness, the example below leverages the Python
library, networkx, for generating a random graph. This library is available by default
within Google Colaboratory and can serve as a convenient environment for
reproducing this example.

# Imports and graph setup


import networkx as nx
from matplotlib import pyplot as plt
plt.rcParams["figure.figsize"] = (10,10)

# Generating a random graph


G = nx.random_graphs.fast_gnp_random_graph(12, 0.4, seed=15)

# Displaying the graph


def draw_graph(G):
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, node_size=500, alpha=0.5)
nx.draw_networkx_labels(G, pos)
nx.draw_networkx_edges(G, pos, width=1.0, alpha=0.5)

draw_graph(G)

This will yield the following output:


Finally, the BFS code is as follows:

# The function implementing the BFS algorithm


def get_shortest_path(graph_data, node_one, node_two):
node_q = [[node_one]]
tested_nodes = []

while node_q:
path = node_q.pop(0)
curr_node = path[-1]

if curr_node not in tested_nodes:


neighbors = []

for pair in list(graph_data.edges): # The edges attribute provides a list of tuples containing all edge
pairs
if curr_node in pair:
neighbors.append(pair[0]) if pair[0] != curr_node else neighbors.append(pair[1])

for n in neighbors:

curr_path = path + [n]


if n == node_two:
return curr_path

node_q.append(curr_path)

tested_nodes.append(curr_node)

return []

# Testing the BFS function


get_shortest_path(G,2,10)

Which will yield:

[2, 0, 5, 10]

Depth-First Search
Published Aug 16, 2023

Contribute to Docs

Depth-first search (DFS) is a traversing algorithm for unweighted graphs.Like BFS


(Breadth-first search), it is a foundational algorithm in graph theory from which many
other algorithms begin.

Features
Some of the features and constraints that define the use and functionality of the DFS
algorithm include the following:

 DFS traverses a graph by following a given path as far as possible. Once a dead
end is reached, the algorithm backtracks to explore the most recent unvisited
neighbours. A stack is used to retrieve the next vertex, to restart a search, when
a dead end is found in any given iteration.
 It is a relatively simple algorithm that can be built upon for finding cycles
within a graph, to conduct a topological sort of vertices, as well as a range of
other useful applications.
 It has a time complexity of O(|V| + |E|), which is the sum of the vertices and
edges.
 It finds a solution, a path between vertices, but it may or may not be optimal
one as in BFS.
 A disadvantage of DFS is that it may get stuck in infinite loop if the depth is
infinite.

Implementation
The example below walks through a basic implementation of DFS that will take a
starting node, an end node, and a graph as arguments. The algorithm will return the
path it navigated between starting and end node provided.

The DFS algorithm starts with the initial node of graph G and goes deeper until it
finds the goal node or a node with no children. Because of the recursive nature of the
traversal process, a stack data structure can be used to implement the DFS algorithm.

The implementation below can be broken down into the following steps:

1. First, initiate a stack with the starting vertex for the traversal.
2. Pop from the stack and set this vertex as the “current” element or node.
3. Now, find the neighboring vertexes (of the current node), and if they haven’t
been visited push them into the stack.
4. If no unvisited vertexes remain, go back and pop a vertex from the stack.
5. Repeat steps 2, 3, and 4 until the stack is empty.

Example

The following is an implementation in Python:

# The visited variable keeps a record of the nodes explored


def dfs(graph,start,goal,stack,visited):
stack.append(start)
visited.append(start)
print('The path traversed is:')
while stack:
# Pop from stack to set current element
element=stack.pop()
print(element,end=" ")
if(element==goal):
break
for neighbor in graph[element]:
if neighbor not in visited:
stack.append(neighbor)
visited.append(neighbor)
# A dictionary representing the illustrated graph
graph={
'A':['C','B'],
'B':['E','D'],
'C':['G','F'],
'D':[],
'E':['I','H'],
'F':[],
'G':['J'],
'H':[],
'I':[],
'J':[]
}
start='A'
goal='J'
visited=[]
stack=[]

dfs(graph,start,goal,visited,stack)

The path traversed is:


ABDEHICFGJ

Best-First Search
Published Mar 31, 2023

Contribute to Docs

Best-first search is a class of search algorithms that explores a graph (the graph can
be weighted or unweighted) for a path from some start node to a goal node. These
algorithms are known as informed search algorithms, meaning that they incorporate
information regarding the location of the goal node relative to any other node. This
information is quantified by an evaluation function denoted by f(x). In a weighted
graph, the evaluation function can combine information regarding the edge weights
and some heuristic function, h(x), that represents a measure from a node (x in this
case) to the goal node. When searching for the goal node, a BFS algorithm (not to be
confused with breadth-first search) selects the node with the most promising value
determined by the evaluation function.

Evaluation Function
An evaluation function estimates the cost to reach the goal node from the current
node. For example, consider the case of a robot having to navigate from point A (start
state) to point B (goal state) in a room containing obstacles and obstructions. The
robot can move one unit of length in the forward direction, backward direction, to the
right, or to the left. The robot wishes to reach point B in the shortest possible time.
What should the robot do? In this case, an evaluation function is defined as the
straight-line distance (Euclidean distance) from any point in the room to point B
ignoring all obstructions. The next position the robot chooses to move to will be the
one with the shortest distance to point B. However, if there is an obstruction
preventing this move, the robot will choose to move to the second-best position in the
room. And if there is an obstruction there, the third-best and so on.

In a weighted graph, the total cost from the start node to the goal node is the sum of
the cost of every visited edge along the path. The values from the evaluation function
do not contribute to the total cost. They are only there to guide the search.

Best-First Search Algorithms


The following is a list of Best-First search algorithms.

 Greedy Best-First Search: A search algorithm where the evaluation function is


strictly equal to the heuristic function, disregarding the edge weights in a
weighted graph.
 A* Search: A search algorithm where the evaluation function takes into
account the edge weights as well as the heuristic measure.
 BEAM Search: A search algorithm where, in a weighted graph, only the n (in
this case, n is known as the beam width) most promising nodes are considered
for further exploration.

A* Search

Published Apr 11, 2023

Contribute to Docs

A* Search is an informed best-first search algorithm that efficiently determines the


lowest cost path between any two nodes in a directed weighted graph with non-
negative edge weights. This algorithm is a variant of Dijkstra’s algorithm. A slight
difference arises from the fact that an evaluation function is used to determine which
node to explore next.

Evaluation Function
The evaluation function, f(x), for the A* search algorithm is the following:
f(x) = g(x) + h(x)
Where g(x) represents the cost to get to node x and h(x) represents the estimated cost
to arrive at the goal node from node x.
For the algorithm to generate the correct result, the evaluation function must
be admissible, meaning that it never overestimates the cost to arrive at the goal node.

The Algorithm
The A* algorithm is implemented in a similar way to Dijkstra’s algorithm. Given a
weighted graph with non-negative edge weights, to find the lowest-cost path from a
start node S to a goal node G, two lists are used:

 An open list, implemented as a priority queue, which stores the next nodes to
be explored. Because this is a priority queue, the most promising candidate
node (the one with the lowest value from the evaluation function) is always at
the top. Initially, the only node in this list is the start node S.
 A closed list which stores the nodes that have already been evaluated. When a
node is in the closed list, it means that the lowest-cost path to that node has
been found.

To find the lowest cost path, a search tree is constructed in the following way:

1. Initialize a tree with the root node being the start node S.
2. Remove the top node from the open list for exploration.
3. Add the current node to the closed list.
4. Add all nodes that have an incoming edge from the current node as child nodes
in the tree.
5. Update the lowest cost to reach the child node.
6. Compute the evaluation function for every child node and add them to the open
list.

Just like in Dijkstra’s algorithm, the lowest cost is updated as the algorithm progresses
in the following way:
current_lowest_cost = min(current_lowest_cost, parent_node_cost + edge_weight)
All nodes except for the start node start with a lowest cost of infinity. The start node
has an initial lowest cost of 0.

The algorithm concludes when the goal node G is removed from the open list and
explored, indicating that a shortest path has been found. The shortest path is the path
from the start node S to the goal node G in the tree.

Note: The algorithm ends when the goal node G has been explored, NOT when it is
added to the open list.
Example
Consider the following example of trying to find the shortest path from S to G in the
following graph:

Each edge has an associated weight, and each node has a heuristic cost (in
parentheses).
An open list is maintained in which the node S is the only node in the list. The search
tree can now be constructed.

Exploring S:

A is the current most promising path, so it is explored next:

Exploring D:
Exploring F:

Notice that the goal node G has been found. However, it hasn’t been explored, so the
algorithm continues because there may be a shorter path to G. The node B has two
entries in the open list: one at a cost of 16 (child of S) and one at a cost of 18 (child
of A). The one with the lowest cost is explored next:
Exploring C:
The next node in the open list is again B. However, because B has already been
explored, meaning a shortest path to B has been found, it is not explored again and the
algorithm continues to the next candidate.

The next node to be explored is the goal node G, meaning the shortest path to G has
been found! The path is constructed by tracing the graph backward from G to S:
Using the A* Algorithm
This algorithm is guaranteed to find a shortest path if one exists. One of the main uses
of this algorithm is route planning. However, there are many other uses.

You might also like