Home Week-3 Week-5

PDSA - Week 4


 

Introduction of Graph

Graph:- It is a non-linear data structure. A graph G consist of a non empty set V where members are called the vertices of graph and the set E where member are called the edges.

G = (V, E)

V = set of vertices

E = set of edges

Example:-

G = (V, E)

V = {A,B,C,D,E}

E = {e1,e2,e3,e4,e5} or {(A,B), (A,C), (B,C), (C,D), (C,E)}

 


 

Basic Terminology of Graph

Vertex (or node):

A vertex or node is a fundamental unit in a graph. In a simple graph, each vertex can be connected to other vertices by edges. Vertices are often represented by circles or dots in visual representations of graphs.

 

Edge:

An edge is a line or connection between two vertices in a graph. Edges can be directed or undirected, and weighted or unweighted. In an undirected graph, edges connect vertices without a specified direction, while in a directed graph, edges have a direction, represented by an arrow. In a weighted graph, edges have a numerical weight or value assigned to them.

 

Degree:

The degree of a vertex is the number of edges that are connected to it. In a directed graph, the degree of a vertex is defined as the sum of the in-degree (number of edges coming into the vertex) and out-degree (number of edges going out of the vertex).

 

Path:

A path is a sequence of vertices connected by edges. A simple path is a path where no vertex is repeated.

 

Cycle:

A cycle is a path that starts and ends at the same vertex.

 

Connectedness:

A graph is said to be connected if there is a path between any two vertices in the graph. A disconnected graph is a graph that is not connected, meaning it can be broken into two or more separate components.

 

Component:

A component is a connected subgraph of a larger graph. In other words, it is a part of the graph that is connected to other vertices or edges within that part, but not connected to the rest of the graph.

 

Directed graph:

A directed graph is a graph where edges have a direction, represented by an arrow. In a directed graph, edges are often called arcs.

 

Undirected graph:

An undirected graph is a graph where edges do not have a direction.

 

Weighted graph:

A weighted graph is a graph where edges have weights or values assigned to them.

 

Adjacent vertices:

Two vertices are adjacent if they are connected by an edge.

 

Incidence:

A vertex is incident on an edge if the vertex is one of the endpoints of the edge.

 

Subgraph:

A subgraph is a graph that is a subset of another graph, with some edges and vertices removed.

 

Complete graph:

A complete graph is a graph where every vertex is connected to every other vertex. In other words, there is an edge between every pair of vertices in the graph.

 

Logical Representation of graph

 


 

Graph Representation in data structure

Adjacency matrix:

An adjacency matrix is a two-dimensional array or list that represents a graph. The matrix has a row and column for each vertex, and the value at position (i, j) indicates whether there is an edge between vertices i and j. If there is an edge, the value is 1, and if there is no edge, the value is 0. This representation is useful for dense graphs, where the number of edges is close to the maximum possible number of edges. In python we can use NumPy array or python list for adjacency matrix.

.

Adjacency list:

An adjacency list is a list of lists where each vertex has a list of its adjacent vertices. Each list contains the vertices that are adjacent to the vertex at that index. This representation is useful for sparse graphs, where the number of edges is much smaller than the maximum possible number of edges. In python we can use dictionary for adjacency list.

 

For unweighted directed graph

 

G = (V, E)

V = [0,1,2,3,4]

E = [(0, 1), (0, 2), (1, 3), (1, 4), (2, 4), (2, 3), (3, 4)]

 

Adjacency matrix representation:

Using NumPy 2d array

Output adjacency matrix (AMat)

 

Using Python nested list

Output adjacency matrix (AMat)

 

Adjacency list representation:

Using python dictionary

Output adjacency list (AList)

 

For unweighted undirected graph

G = (V, E)

V = [0,1,2,3,4]

E = [(0, 1), (0, 2), (1, 3), (1, 4), (2, 4), (2, 3), (3, 4)]

 

Adjacency matrix representation:

Using NumPy 2d array

Output adjacency matrix (AMat)

 

Using Python nested list

Output adjacency matrix (AMat)

 

Adjacency list representation:

Using python dictionary

Output adjacency list (AList)

 


 

Graph Traversing Algorithm

Graph traversal is the process of visiting all the vertices (nodes) of a graph. There are two commonly used algorithms for graph traversal:

  1. Breadth-First Search (BFS)

  2. Depth-First Search (DFS)

 

Breadth First Search(BFS)

The Breadth First Search (BFS) algorithm is used to traverse a graph or a tree in a breadth-first manner. BFS starts at the root node and explores all the neighboring nodes at the current depth before moving on to the nodes at the next depth. This is done by using a queue data structure. The algorithm marks each visited node to avoid revisiting it.

Algorithm

Here are the steps for the BFS algorithm:

  1. Choose a starting node and enqueue it to a queue.

  2. Mark the starting node as visited.

  3. While the queue is not empty, dequeue a node from the front of the queue.

  4. For each of the dequeued node's neighbors that are not visited, mark them as visited and enqueue them to the queue.

  5. Repeat steps 3-4 until the queue is empty.

To keep track of the traversal order, you can add each visited node to a list as it is dequeued from the queue.

 

Complexity is O(V2) using adjacency matrix, O(V+E) using adjacency list, Where V is number of vertices and E is number of edges in Graph.

 

Working Visualization

 

Implementation BFS for adjacency list of graph

Output

 

Code Execution Flow

 

Implementation BFS for adjacency matrix of graph

Output

 

Code Execution Flow

 

 

Find parent of each vertex using BFS

Parent information is useful to determine shortest path from vertex v in term of number of edges or vertex in path.

Output

 

 

Find level number of vertices using BFS

Maintain level information to record length of the shortest path, in terms of number of edges or vertex.

Output

 


 

Depth First Search(DFS)

The Depth First Search (DFS) algorithm is used to traverse a graph or a tree in a depth-first manner, exploring as far as possible along each branch before backtracking. This is done by using a stack data structure.

 

Algorithm

Here are the steps for the Recursive DFS algorithm:

  1. Choose a starting node and mark it as visited.

  2. For each unvisited neighbor of the starting node, recursively call the DFS algorithm starting from that neighbor.

  3. Repeat step 2 for all unvisited neighbors.

 

Here are the steps for DFS using a stack:

  1. Create a stack S and a set visited to keep track of visited nodes.

  2. Push the starting node onto the stack S.

  3. While S is not empty, pop the top element u from S.

  4. If u is not visited, mark it as visited and do the following:

    • Perform any processing on the node u.

    • Get all unvisited neighbors v of u and push them onto S.

  5. Repeat steps 3-4 until S is empty.

To keep track of the traversal order, you can add each visited node to a list as it is visited.

 

Complexity is O(V2) using adjacency matrix, O(V+E) using adjacency list, Where V is number of vertices and E is number of edges in Graph.

 

Working Visualization

 

Implementation of DFS for adjacency list of graph

DFS using Stack for adjacency list of graph

Output

 

Code Execution Flow

 

DFS Recursive (without using external stack)

Output

 

Code Execution Flow

 

DFS global for adjacency list of graph

Output

 

Implementation of DFS for adjacency matrix of graph

Output

 

Code Execution Flow

 

 

DFS global for adjacency matrix of graph

Output

 


 

Application of BFS and DFS

Find Connected Components in graph using BFS

The Connected Components in a Graph using BFS algorithm is used to find all the connected components in an undirected graph.

Algorithm

Here are the steps for the Connected Components in Graph using BFS algorithm:

  1. Create a queue and an array visited to keep track of the visited nodes in the graph.

  2. Initialize dictionary component[v] = -1 for each vertex v in the graph

  3. For each unvisited node u in the graph, perform a BFS traversal starting from u and assign new component id to all the visited nodes as a single connected component in component dictionary.

  4. Repeat steps 3 until all nodes in the graph are visited.

  5. Return component dictionary.

 

For given graph

 

Implementation of Find Connected Components in graph using BFS

Output

 

 

Pre and Post numbering using DFS

The Pre and Post numbering using DFS algorithm is used to assign a unique number to each node in a graph during a Depth-First Search (DFS) traversal.

Algorithm

Here are the steps for the Pre and Post numbering using DFS algorithm:

  1. Initialize a counter count to 0 and two dictionary pre and post to keep track of the pre and post numbers of the nodes.

  2. For each unvisited node u in the graph, perform a DFS traversal starting from u.

  3. During the DFS traversal, visit each unvisited neighbor v of the current node u and recursively perform a DFS traversal starting from v.

  4. Increment the count and assign it as the pre number of the current node u.

  5. After all neighbors of the current node u have been visited, increment the count again and assign it as the post number of the current node u.

  6. Repeat steps 3-5 until all nodes in the graph are visited.

  7. Return the pre and post arrays.

 

For given graph

Implementation of Pre and Post numbering on graph using DFS

Output

 

Classifying tree edges in directed graphs using pre and post numbering

Tree edge/forward edge:

Edge (u, v) is tree/forward edge, if Interval [pre(u), post(u)] contains [pre(v), post(v)]

 

Back edge:

Edge (u, v) is back edge, if Interval [pre(v), post(v)] contains [pre(u), post(u)]

Back edges generate cycles

 

Cross edge:

Edge (u, v) is cross edge, if Intervals [pre(u), post(u)] and [pre(v), post(v)] are disjoint

 


 

Directed Acyclic Graph(DAG)

A Directed Acyclic Graph(DAG) is a directed graph with no directed cycles. That is, it consists of vertices and edges, with each edge directed from one vertex to another, such that following those directions will never form a closed loop.

Directed acyclic graphs are a natural way to represent dependencies

 

Topological Sort

The Topological Sort algorithm is used to sort the nodes of a directed acyclic graph (DAG) in such a way that for every directed edge from node u to node v, node u comes before node v in the sorting order.

Algorithm

Here are the steps for the Topological Sort algorithm:

  1. Compute the in-degree of each node in the graph.

  2. Enqueue all nodes with in-degree 0 to a queue.

  3. While the queue is not empty, dequeue a node from the front of the queue and add it to the sorted list.

  4. For each of the dequeued node's neighbors, decrement their in-degree by 1.

  5. If any of the dequeued node's neighbors now have in-degree 0, enqueue them to the queue.

  6. Repeat steps 3-5 until the queue is empty.

If the graph contains a cycle, the Topological Sort algorithm cannot produce a valid sorting order because it is impossible to order the nodes such that all edges point forward. In this case, the algorithm will terminate with an error or produce an incomplete sorting order.

To keep track of the sorting order, you can add each dequeued node to a list as it is dequeued from the queue.

 

Complexity is O(V2) using adjacency matrix, O(V+E) using adjacency list, Where V is number of vertices and E is number of edges in Graph.

 

Working Visualization

For given graph(DAG)

 

Implementation of Topological Sort for Adjacency list

Output

 

Code Execution Flow

 

Implementation of Topological Sort for Adjacency matrix

Output

 


 

Longest Path in DAG

The Longest Path in an unweighted Directed Acyclic Graph (DAG) algorithm is used to find the maximum length path in a DAG.

Algorithm

Here are the steps for the Longest Path in an Unweighted DAG algorithm:

  1. Topologically sort the nodes in the graph using the Topological Sort algorithm.

  2. Initialize a distance array with all nodes set to 0 except the source node, which is set to 0.

  3. For each node u in the topologically sorted order, iterate through its incoming edges (v, u) and update the distance of node u as max(dist[u], dist[v] + 1).

  4. The longest path length will be the maximum value in the distance array.

 

Complexity is O(V+E) using adjacency list, Where V is number of vertices and E is number of edges in Graph.

 

Working Visualization

For given graph(DAG)

 

Implementation for Longest Path on DAG

Output

Code Execution Flow

 


 

Visualization of graph algorithms using visualgo

Select the BFS/DFS/Topological sort algorithm from left menu to visualize the working.

Source - https://visualgo.net/en/dfsbfs