CS6131:Advanced Algorithm Design and Analysis
MSc in computer science
King Khaled University
Graph Theory
Raghad Alwadie
Rana Hussain
Dalal Alqahtani
Description of graph
A Graph is a non-linear data
structure consisting of
vertices and edges.
Graph is composed of a set
of vertices( V ) and a set of
edges( E ).
The graph is denoted by
G(E, V).
2
Representation of graph
1- The adjacency-matrix representation of a graph G = (V, E) :
assumes that the vertices
are numbered
1, 2, … , |V|
Then the adjacency-
matrix will be :
Running time of graph is O(𝑉 2 ) ,each vertex of the graph has one row
and one column.
Representation of graph
2- The adjacency-list representation of a graph G = (V, E) :
For each u ∈ V, the
adjacency list Adj[u]
contains all the vertices v
such that there is an edge
(u, v) ∈ E.
Running time of graph is O(V+E)
Graph-searching Algorithms
• Standard graph-searching
algorithms.
• Breadth-first Search (BFS).
• Depth-first Search (DFS).
• These are for general graphs,
there are other different
algorithms to find shortest
path for weighted graph ,
which is called MST .
Implementation of graph using adjacency list using C++
#include <bits/stdc++.h>
using namespace std;
int v= 6 ; // the number of graph node
// function to add edge in between source and distination node
void AddEdge(vector<int> Graph [] , int source , int destination){
Graph[source].push_back(destination);
Graph[destination].push_back(source);
}
// PrintGraph function
void PrintGraph(vector<int> Graph []){
for (int source =0 ; source < v; source ++ ){
cout<< source <<" ---> " ;
for (int neighbours : Graph [source] ){
cout<< neighbours <<" ";
}
cout<<endl;
}}
int main() {
• Total number of vector <int> Graph[v]; // declare the adjaceny list
//add all the edges in the graph
vertices = 6 AddEdge (Graph , 0,1);
• Total number of AddEdge (Graph , 0,5);
AddEdge (Graph , 1,2);
Edges = 7 AddEdge (Graph , 1,5);
• Type of graph is AddEdge (Graph , 3,5);
AddEdge (Graph , 3,4);
undirected graph AddEdge (Graph , 2,4);
//call the PrintGraph function
PrintGraph(Graph) ;
}
Implementation of graph using adjacency list using C++
Output:
0 ---> 1 5
1 ---> 0 2 5
2 ---> 1 4
3 ---> 5 4
4 ---> 3 2
5 ---> 0 1 3
• Total number of
vertices = 6
• Total number of
Edges = 7
• Type of graph is
undirected graph
TRY THE CODE
DFS
• Depth First Search.
• Time complexity: O(V + E)
• There is two way to implement DFS :
1) Using Recurrence
2) Using Iteration
Recurrence of DFS
1
So, we need to make array for each
0 3 4 visited nodes . graphs may contain
cycles, so a node might be visited twice.
To avoid processing a node more than
2 once, use a Boolean visited array.
DFS(G,0):01342 . 0’s neighbor is 1
Pseudocode:
1
0 3 4
DFS(G,0):01342 . 1’s neighbor is 0 and 3 .
The big issue is when you iterate through
node 1’s neighbors we found 0 which is
already visited which make infinite cycle
recursion .
VISITED VISITED
1 0 1 2 3 4
T F F F F
0 3 4
DFS(G): 0 1 0 1 2 3 4
2 T T F T T
0 3 4
VISITED
0 1 2 3 4
1 DFS(G):0,1,3, 4
T T F F F 2
0 3 4
DFS(G):0, 1 VISITED
2 1
0 1 2 3 4
T T T T T
0 3 4
VISITED
1 0 1 2 3 4
T T F T F
2 DFS(G):0,1,3,4, 2
0 3 4
DFS(G):0,1, 3
2
Recurrence of DFS C++ code
// DFS recursive function
void DFS(vector <int> Graph[], vector <bool> &visited, int current_node){
cout<<current_node<<" ";
// mark all current_node visited so we do not process the same node
visited[current_node]= true;
// graph[current_node] would be containing the neighbour of current_node
for(int neighbour : Graph[current_node]){
if (!visited[neighbour]){
// if neighbour is not visited , invoke the dfs for the neighbour
DFS(Graph, visited,neighbour);
}
}
}
Invoke the function in main: int main (){
// declare visited vector
vector <bool> visited (v,false);
// call the dfs function
DFS(Graph, visited,0);
}
TRY THE CODE
iterative of DFS
Using stack data structure to restore the nodes which are visited
in order of LIFO
source vertex : 0
STACK 0 1 STACK
DFS : 0 0 1
DFS : 0,1
5
0 3 2 0 5
3 2
1
4 6 4 6
VISITED
VISITED
VISITED 1 VISITED
0 1 2 3 4 5 6 UN VISITED 0 1 2 3 4 5 6
0 T F F F F F F 3 T T F T F F F UN VISITED
Explore : - Explore : -
0 1 0 1
STACK STACK
DFS : 0,1,5 5 DFS : 0,1,5,6 5
3 2 3 2
4 6 0
0 4 6
1
5 1
5 6
5 VISITED
6 VISITED 6
2 VISITED UN VISITED 2 VISITED UN VISITED
0 1 2 3 4 5 6 0 1 2 3 4 5 6
3 T T T T F T T 3 T T T T F T T
Explore : - Explore : -
0 1 0 1
STACK STACK DFS : 0,1,5,6,4,2
DFS : 0,1,5,6,4 5 5
3 2 0 3 2
1
0 4 6 4 6
5 6
1
5 6 4
4 VISITED 2 VISITED
4
2 VISITED 2 VISITED
0 1 2 3 4 5 6 UN VISITED 0 1 2 3 4 5 6 UN VISITED
3 T T T T T T T 3 T T T T T T T
Explore : 4 0 1 Explore : 2
DFS : 0,1,5,6,4,2,3 0 1
STACK DFS : 0,1,5,6,4,2,3 STACK
5 5
0 3 2 0 3 2
1 1
4 6 5 4 6
5 6 6
4 4
2 VISITED
2 3 VISITED
3
VISITED VISITED
0 1 2 3 4 5 6 UN VISITED 0 1 2 3 4 5 6 UN VISITED
3 T T T T T T T T T T T T T T
iterative of DFS C++ code
void DFS(int s,vector<int> Graph [] )
{
// Initially mark all vertices as not visited
vector<bool> visited(v, false);
// Create a stack for DFS
stack<int> stack;
// Push the current source node.
stack.push(s);
while (!stack.empty())
{
// Pop a vertex from stack and print it
int s = stack.top();
stack.pop();
/* Stack may contain same vertex twice. So we need to print the popped item only if it is not visited.*/
if (!visited[s])
{
cout << s << " ";
visited[s] = true;
/* Get all adjacent vertices of the popped vertex s , If a adjacent has not been visited, then push it to
the stack.*/
for(int neighbour : Graph[s])
if (!visited[neighbour])
stack.push(neighbour);
}
}}
Invoke the function in main: int main (){
// call the dfs function
DFS(0,Graph);
}
TRY THE CODE
Running time
The time complexity is O(|V|+ |E|).Note that O(E)
may vary between O(1) and O(V²), depending on
how dense the graph is.
Recursion Iterative
More standard More generalizable to other
implementation of DFS. graph traversal algorithm
Its cleaner and easier to that require an iterative
read approach
BFS
BFS(Breadth First Search) uses Queue data structure(FIFO).
Time Complexity: O(V+E)
Recurrence of BFS
It is awkward to use recursion in BFS ,and no one dose it .
Recurrence of BFS can not be worked without auxiliary data
structure .
Iterative of BFS
Using queue data structure to restore the nodes which are visited
in order of FIFO
source vertex : 0 Explore : 0
0 1 0 1
QUEUE QUEUE
5 5
0 3 2 0 1 3 3 2
BFS : 0 4 6 BFS : 0,1,3 4 6
0
0
VISITED 1 3 VISITED
UN VISITED UN VISITED
Explore : 1 Explore : 3
0 1 0 1
QUEUE QUEUE
5 5
0 1 3 2 5 6 3 2 0 1 3 2 5 6 4 3 2
BFS : 0,1,3,2,5,6, 4 6 BFS : 0,1,3,2,5,6,4 4 6
0 0
1 3 VISITED 1 3 VISITED
2 5 6 UN VISITED
2 5 6 4 UN VISITED
Explore : 2 Explore : 5
0 1 0 1
QUEUE QUEUE
5 5
0 1 3 2 5 6 4 3 2 0 1 3 2 5 6 4 3 2
BFS : 0,1,3,2,5,6,4 4 6 BFS : 0,1,3,2,5,6,4 4 6
0 0
1 3 VISITED 1 3 VISITED
2 5 6 4 UN VISITED
2 5 6 4 UN VISITED
Explore : 6 Explore : 4
0 1 0 1
QUEUE QUEUE
5 5
0 1 3 2 5 6 4 3 2 0 1 3 2 5 6 4 3 2
BFS : 0,1,3,2,5,6,4 4 6 BFS : 0,1,3,2,5,6,4 4 6
0 0
1 3 VISITED 1 3 VISITED
2 5 6 4 UN VISITED
2 5 6 4 UN VISITED
Iterative of BFS C++ code
void bfs(int u,vector<int> Graph [])
{
// Initially mark all vertices as not visited
vector<bool> vis(v, false);
// Create a queue for BFS
queue<int> q;
// Push the current source node.
q.push(u);
vis[u] = true;
while (!q.empty()) {
// Pop a vertex from queue and print it
int f = q.front();
q.pop();
cout << f << " ";
// Enqueue all adjacent of f and mark them visited
for(int neighbour : Graph[f]) {
if (!vis[neighbour]) {
q.push(neighbour);
vis[neighbour] = true;
}
}
}
Invoke the function in main: int main (){
// call the bfs function
bfs (0,Graph);
}
TRY THE CODE
Running time
The time complexity is O(|V| + |E|).Note that O(E)
may vary between O(1) and O(V²), depending on how
dense the graph is.
Recursion Iterative
It is not practical . More standard implementation
Cannot be used without of BFS.
auxiliary data structure . The BFS can not work without
auxiliary data structure .
Deciding when to use DFS or BFS
We can’t have fixed rules for using BFS or DFS.
It totally depends on the problem we are trying to solve,but
we can make some general intuition.
1/ We will prefer to use BFS when we know that our solution
might lie closer to the starting point or if the graph has greater
depths . Or If we have multiple starting points and the problem
requires us to start traversing all those starting points parallelly
2/ We will prefer to use DFS when we know our solution
might lie farthest from the starting point or when the graph has
a greater width. in path-finding algorithms to find paths
between nodes.
Real life problem using graph
Sudoku is a single — player
logic based puzzle. A Sudoku
puzzle is a grid of 81 cells,
which is divided into 9 rows,
columns and regions(or blocks)
The goal is to place the
numbers from 1- 9 into empty
cells in such a way, that in every
row, every column and every
region (3 x 3 block) each
number appears only once
Graph coloring is an assignment of different colors ( or labels)
to the vertices of a graph, such that no 2 adjacent (connected)
vertices have the same color
Python Code for sudoku implementation
Algorithm for Graph Coloring Problem
• Create a recursive function that takes the graph, current vertex index,
number of vertices and output color array.
• If the current vertex index is equal to number of vertices. Print the color
configuration in output array.
• Assign color to a vertex (1 to m). { m = the number of colors you want to
color the Graph }
• For every assigned color, check if the configuration is safe, (i.e. check if
the adjacent vertices do not have the same color) recursively call the
function with next index and number of vertices
• If any recursive function returns true break the loop and return true.
• If no recursive function returns true then return false.
References :
• https://www.hackerearth.com/practice/algorithms/graphs/breadth-
first-search/tutorial/
• https://www.hackerearth.com/practice/algorithms/graphs/depth-first-
search/tutorial/
• https://www.onlinegdb.com/online_c++_compiler
• https://www.geeksforgeeks.org/difference-between-bfs-and-dfs/
• https://www.programiz.com/dsa/graph-bfs