3.3Implementing Graph Traversal Using Queues and Stacks
A graph can be represented as either an adjacency matrix or an adjacency list. with a graph G = (V,E), the number of nodes |V| is denoted as n and the number of edges |E| is denoted as m. When discussing the representations of graphs, our aim is to get a representation that allows us to implement the graph in a polynomial running time. The number of edges m is always less than or equal to n2. Since most of graphs are connected, the graphs considered in the discussion of the representation of graphs have at least m≥ n-1 edges. The goal we have in the implementation of graphs is O(m+n), and is called linear time.
Some Definitions:
- Adjacency matrix: it's an n X n matrix A where A[u,v] is equal to 1 if the graph contains the edge (u,v), and 0 otherwise. This representation allows us to represent graphs in O(1) time if a the edge (u,v) is in the graph.
- Disadvantages:
- The representation takes Θ(n2) space.However, when the graph has many fewer edges than n2, more compact (and better) representations are possible
- Many graph algorithms need to examine all edges incident to a given node v. With the adjacency matrix, this operation takes Θ(n) time. More efficient algorithms exist solve this problem.
- Adjacency list: With this representation, there is record for each node v, containing a list of the nodes to which v has edges. It used when the graphs have less than n2 edges.
- With this representation we have an array Adj, where Adj[v] contains a record of all nodes adjacent to the node v.
- Comparison of the Adjacency list and the Adjacency matrix:
- Since an Adjacency matrix requires an n X n matrix, it is O(n2) space
- An Adjacency list takes O(m+n) space.
- In the Adjacency list, it takes O(nv) time to check if a particular edge (u,v) is in the graph, while the same operation takes O(1) time for an adjacency matrix.
- The degree nv of a node v is the number of incident edges it has.
- The length of the list at Adj[v] is nv
- The sum of the degrees in a graph = 2m
Queues and Stacks
- With A queue, we use the First in, First Out(FIFO) concept
- The new element is added to the end of the list
- For a stack, we use the Last in, First Out(LIFO) concept
- The new element is added at the front of the list
- In both implementations, the doubly linked lists used maintain a FIRST and a LAST pointers which allows constant time insertions
Implementation and analysis of the BFS and the DFS
- BFS:
BFS(s):
Discovered[v] = false, for all v
Discovered[s] = true
L[0] = {s}
layer counter i = 0
BFS tree T = {}
while L[i] != {}
L[i+1] = {}
For each node u ∈ L[i]Consider each edge (u,v) incident to u
if Discovered[v] == false thenDiscovered[v] = true
Add edge (u, v) to tree T
Add v to the list L[i + 1]Endif
Endfor
end while
This implementation of the BFS takes O(m+n) if the graph is given by the adjacency list representation.
- DFS:
DFS(s):
Initialize S to be a stack with one element s
Explored[v] = false, for all v
Parent[v] = 0, for all v
DFS tree T = {}
while S != {}
Take a node u from S
if Explored[u] = falseExplored[u] = true
Add edge (u, Parent[u]) to T (if u ≠ s)
for each edge (u, v) incident to uAdd v to the stack S
Parent[v] = uEndfor
Endif
end while
This implementation of the DFS takes O(m+n) if the graph is given by the adjacency list representation.
Thus when using a DFS or a BFS, one can easily implement graph traversals in linear time(O(m+n)).
This section was interesting too, I give it an 8/10.