Introduction to Graphs
Programming Club
What is a Graph?
Anything that can be modelled as comprising of Vertices and Edges can be called a Graph.
Examples are:
A number of cities(vertices) connected by roads(edges).
Computers(vertices) in a LAN Network connected by ethernet cables(edges).
A Graph with 6 nodes and 7 edges
Terminology
see also: Graph theory definitions
Undirected Graphs : If there is an edge between u-v , it can be traversed both ways,i.e, from u to v and from v to u. Eg. A two way road.
Directed Graphs : Unlike undirected graphs an edge between u and v implies that one can go only from u to v and not the other way around. The edges are denoted with arrows like u->v.
Eg. Following someone on a social networking website like Instagram. Following a person does not imply the other person follows you. Much like a directed Graph.
Terminology
see also: Graph theory definitions
Unweighted Graphs: In these each edge is assigned a unit weight, i.e, all edges are equal with respect to weight.
Weighted Graphs: In these,each edge is assigned some weight.It may be positive or negative as per the situation.
Representation of Graphs
G(V,E):
A graph with V vertices & E edges.
Representation in programming.
- Adjacency Matrix
- Adjacency List
Adjacency Matrix
We will construct a VxV Matrix to store information about the edges of the graph.
Let M denote the adjacency matrix. If M[u][v] is 1 it denotes that there is an edge going from u to v. Similarly if it is 0 it denotes that there is no edge.
Note that in an undirected graph M[u][v] = 1 implies that M[v][u] = 1.
The space needed is O(V^2).
Advantages: Easy Representation.
More useful for smaller and denser(more edges) graphs.
Disadvantage: For Larger Graphs with number of edges much lesser than V^2 , the matrix is composed of a large number of 0s, leading to efficiency in allocating memory and time taken in other algorithms (mentioned ahead).
Adjacency List
Unlike Adjacency Matrices , here we only keep information about the edges in the graph and not about those that aren’t there. We maintain V linked lists , one for each vertex. If there is an edge (u,v) then we append v to u’s linked list.The size of Adjacency List is O(E).
We can implement this using a vector of vectors.
vector< vector<int> > adj(N); // N: Max no. of nodes.
for each edge E(u,v):
adj[u].push_back(v)
Graph Algorithms
The Problem:
Given a graph G with V vertices and E edges , answer the following types of questions:
1. Given a vertex u and v , tell us whether we can reach v from u traveling across a sequence of edges.
2.Given a vertex v tell us how many vertices from the graph can it reach ,i.e, the number of vertices in its connected component.
3.Given a graph, tell us how many connected components does it have.
We can solve these using Depth First Search (DFS) and Breadth First Search (BFS).
DFS
DFS is the more intuitive of the two search algorithms. It goes like this:
This algorithm finds all the vertices that can be reached from the initial search vertex.
Time Complexity: As we visit each vertex once and travel over each edge once the complexity is O(V+E). (Note that the complexity is O(V+E) only if we use Adjacency Lists.It is O(V^2 ) if we use Adjacency Matrices.)
DFS(v):
Mark v as visited
For every neighbour u of v
If u is unvisited
DFS(u)
BFS
In BFS , we traverse the graph in a different manner. Assuming we have started the search from a source s, let us a new term: Layer. A vertex belongs to a layer i if it can reach the source in a minimum of i steps and no less. Hence , the Source belongs to layer 0. All its neighbours belong to layer 1. And so on. We process the graph layer by layer. To do this we use a queue. First we add the source to the queue and mark it as Layer 0. Then we do this repeatedly. We pop off the front of the queue.Let me call this vertex u.For all unvisited neighbours of u we mark them as Layer[u]+1 and push them to the back of the queue. When the queue is empty we have processed all vertices that could be reached from u. Complexity: O(V+E), same as DFS.
Show me Code
vector< vector<int> > adj(N); // N: max no. of nodes
vector<int> visited(N, 0);
void dfs(int v){
if(!visited[v]){
visited[v]=1;
// Solve
for(auto i:adj[v])
dft(i);
}
}
vector< vector<int> > adj(N); // N: max no. of nodes
vector<int> visited(N, 0);
void bfs(int v){
queue<int> q;
q.push(v);
while(!q.empty(){
int v = q.front();
q.pop();
// solve
if(!visited[v]){
visited[v]=1;
for(auto i:adj[v])
if(!visited[v])
q.push(i);
}
}
}
Let's solve some problems
Extra readings
Introduction to Graphs
By APwhitehat
Introduction to Graphs
- 497