Professional Documents
Culture Documents
Introduction To Graph - C
Introduction To Graph - C
Introduction
When we talk about graphs, what comes to mind are the conventional graphs used to model data. In
computer science, the term “graph” has a completely different meaning.
The graph data structure plays a fundamental role in several applications such as GPS, neural
networks, peer to peer networks, search engine crawlers, and even social networking websites.
This section will explore their functionality and power. We will also look at how they are used to
solve a diverse range of problems.
Graph Structure
A graph is a set of nodes that are connected to each other in the form of a network. First of all, we’ll
define the two basic components of a graph.
Vertex
A vertex is the most essential part of a graph. A collection of vertices forms a graph. In that sense,
vertices are similar to linked list nodes.
Edge
An edge is the link between two vertices. It can be uni-directional or bi-directional depending on
your graph. An edge can also have a cost associated with it (will be discussed in detail later).
Graph Terminologies
Degree of a Vertex: The total number of edges connected to a vertex. There are two types of
degrees:
Parallel Edges: Two undirected edges are parallel if they have the same end vertices. Two directed
edges are parallel if they have the same origin and destination.
Self-Loop: This occurs when an edge starts and ends on the same vertex.
Adjacency: Two vertices are said to be adjacent if there is an edge connecting them directly.
In the illustration above, the in-degree of both a and b is 1. Same goes for the out-degree of the
vertices. The in-degree and out-degree for c is 2 as it contains a self loop.
1. Undirected
2. Directed
Undirected Graph #
In an undirected graph, the edges are by default, bi-directional. For a pair (2, 3), there exists an edge
between vertex 2 and 3 without any specific direction. You can go from vertex 2 to 3 or ,vice versa,
from 3 to 2 .
Let’s calculate the maximum possible edges for an undirected graph. We are denoting an edge
between vertex a and b as (a, b). So, the maximum possible edges of a graph with n vertices will be
all possible pairs of vertices of that graph, assuming that there are no self loops.
If a graph has n vertices, then there are C(n,2) possible pairs of vertices according to Combinatorics.
Solving C(n,2) by binomial coefficients gives us \frac{n(n-1)}{2}2n(n−1). Hence, there are \frac{n(n-
1)}{2}2n(n−1) maximum possible edges in an undirected graph.
Directed Graph #
In a directed graph, the edges are unidirectional. For a pair (2, 3), there exists an edge from
vertex 2 towards vertex 3 and the only way to traverse is to go from 2 to 3, not the other way
around.
This changes the number of edges that can exist in the graph. For a directed graph with n vertices,
the minimum number of edges that can connect a vertex with every other vertex is n-1. This
excludes self loops.
If you have n vertices, then all the possible edges become n*(n-1).
Representation of Graphs
Ways to Represent a Graph
The two most common ways to represent a graph are:
Adjacency Matrix
Adjacency List
Adjacency Matrix
The adjacency matrix is a two dimensional matrix where each cell can contain a 0 or 1. The row and
column headings represent the vertices.
For a directed graph, the usual convention is to think of the rows as sources and the columns as
destinations.
Adjacency List
An array of linked list is used to store all the edges in the graph. The size of the array is equal to the
number of vertices.
Each index in this array represents a specific vertex in the graph. The entry at index i of the array
contains a linked list containing the vertices that are adjacent to vertex i.
As you can see in the above figure, the linked list at index 0 contains vertices 1 and 2 to represent
the fact that vertices 1 and 2 are adjacent to vertex 0.
Graph Implementation
This lesson will cover the implementation of a unidirectional graph via adjacency list in C++. We will
also go through the time complexity of basic graph operations.
Introduction
At this point, we’ve understood the theoretical logic behind graphs. In this lesson, we will use the
knowledge we have to implement the graph data structure in C++. Our graph will have directed
edges.
The implementation will be based on the adjacency list model. The linked list class we created
earlier will be used to represent adjacent vertices.
As a refresher, here is the illustration of the graph we’ll be producing using an adjacency list:
class Graph {
private:
//Total number of vertices
int vertices;
//definining an array which can hold multiple LinkedLists equal to the number
of vertices in the graph
LinkedList * array;
public:
Graph(int vertices) {
//Creating a new LinkedList for each vertex/index of the array
array=new LinkedList[vertices];
}
};
int main() {
Graph myGraph(5);
return 0;
}
Additional Functionality
3. class Graph {
4. private:
5. int vertices;
6. LinkedList * array;
7. public:
8. Graph(int v) {
9. array=new LinkedList[v];
10. vertices=v;
11. }
12.
13. void addEdge(int source, int destination) {
14. if (source < vertices && destination < vertices)
15. array[source].insertAtHead(destination);
16. }
17.
18. void printGraph() {
19. cout << "Adjacency List of Directed Graph" <<endl;
20. Node*temp;
21. for(int i=0; i<vertices; i++) {
22. cout << "|" << i << "| => ";
23. temp = (array[i]).getHead();
24.
25. while(temp != NULL) {
26. cout << "[" <<temp->data << "] -> ";
27. temp = temp->nextElement;
28. }
29. cout <<"NULL"<<endl;
30. }
31. }
32. };
33.
34. int main() {
35. Graph g(4);
36. g.addEdge(0, 1);
37. g.addEdge(0, 2);
38. g.addEdge(1, 3);
39. g.addEdge(2, 3);
40. cout << endl;
41. g.printGraph();
42. return 0;
43. }
addEdge (source, destination)
Thanks to the graph constructor, source and destination are already stored as
indices of our array. This function simply inserts a destination vertex into
the adjacency linked list of the source vertex by running the following line
of code:
array[source].insertAtHead(destination)
printGraph()
This function uses a simple nested loop to iterate through the adjacency list.
Each linked list is being traversed here.
Undirected graph
In the case of an undirected graph, we will have to create an edge from the
source to the destination and from the destination to the source, making it
a bidirectional edge:
array[source].insertAtHead(destination)
array[destination].insertAtHead(source)
Below, you can find the time complexities for the 4 basic graph functions.
Note, that in this table V means the total number of vertices and E means the total number of edges
in the Graph.
Addition operations in adjacency lists take constant time as we only need to insert at the head node
of the corresponding vertex.
Removing an edge takes O(E) time, because in the worst case, all the edges could be at a single
vertex and, hence, we would have to traverse all E edges to reach the last one.
Removing a vertex takes O(V + E) time because we have to delete all its edges and then reindex the
rest of the list one step back in order to fill the deleted spot.
Adjacency Matrix
Edge operations are performed in constant time as we only need to manipulate the value in the
particular cell.
Vertex operations are performed in O(V2) since we need to add rows and columns. We will also
need to fill all the new cells.
Comparison
Both representations are suitable for different situations. If your model frequently manipulates
vertices, the adjacency list is a better choice.
If you are dealing primarily with edges, the adjacency matrix is the more efficient approach.
Keep these complexities in mind because they will give you a better idea about the time
complexities of the several algorithms we’ll see in this section.
The bipartite graph is a special member of the graph family. The vertices of this graph are divided
into two disjoint parts in such a way that no two vertices in the same part are adjacent to each
other.
The bipartite graph is a type of k-partite graph where k is 2. In a 5-partite graph, we would have 5
disjoint sets and in members of a set would not be adjacent to each other.
Can a Cyclic Graph be Bi-Partite?
A cyclic graph is one in which the edges form a cycle between the vertices. If you traverse a cyclic
graph, you would come back to a vertex which you have already visited. Here’s an example:
Yes it can.
In the illustration below, you can see that the graph has an even number of nodes, which means that
they can be divided into two disjoint sets having non-adjacent vertices.
If there were an odd number of vertices, the nodes could never be divided into two disjoint and non-
adjacent sets.
All the acyclic graphs can be bi-partite, but in the case of cyclic graphs, they must contain an even
number of vertices.
Types of bipartite graphs
Star Graph
Acyclic Graph
Path Graph
In order to understand these algorithms, we will have to view graphs from a slightly different
perspective.
Any traversal needs a starting point, but a graph does not have a linear structure like lists or stacks.
So how do we give graph traversal a better sense of direction?
This is where the concept of levels is introduced. Take any vertex as the starting point. This is the
lowest level in your search. The next level consists of all the vertices adjacent to your vertex. A level
higher would mean the vertices adjacent to these nodes.
With this is in mind, let’s begin our discussion on the two graph traversal algorithms.
The BFS algorithm earns its name because it grows breadth-wise. All the nodes in a certain level are
traversed before moving on to the next level.
The level-wise expansion ensures that for any starting vertex, you can reach all others, one level at a
time.
1→2→3→6→4→5
This happens because there is a bidirectional link between 1 and 6, making them adjacent to each
other.
The DFS algorithm is the opposite of BFS in the sense that it grows depth-wise.
Starting from any node, we keep moving to an adjacent node until we reach the farthest level, then
we move back to the starting point and pick another adjacent node. Once again, we probe till the
farthest level and move back. This process continues until all nodes are visited.