Graph is a data structure that consists of the following two components:
Common types and terms:
Graphs model many real-life systems such as road networks, telephone and circuit networks, and social networks (for example, in social sites each person is a vertex and relationships are edges). A node (vertex) can be represented as a structure containing fields such as id, name and other attributes.
Following is an example undirected graph with 5 vertices.
The two most commonly used representations of a graph are:
Other representations include the incidence matrix and incidence list. Choice of representation depends on the operations required and the graph density (sparse vs dense).
An adjacency matrix is a 2-D array of size V × V, where V is the number of vertices. Let the array be adj[][]. For an unweighted graph, adj[i][j] = 1 indicates an edge from vertex i to vertex j (and 0 otherwise). For a weighted graph, adj[i][j] = w indicates an edge of weight w.
An adjacency matrix for an undirected graph is always symmetric.
The adjacency matrix for the example graph is:
Pros
Cons
An adjacency list uses an array (or vector) of lists. The array size equals the number of vertices; each entry stores the list of neighbours of the corresponding vertex. This representation is space-efficient for sparse graphs and can store weights in list nodes.
Adjacency list representation of the example graph is:
Example C implementation (adjacency list for an undirected graph):
// A C Program to demonstrate adjacency list representation of graphs #include <stdio.h> #include <stdlib.h> // A structure to represent an adjacency list node struct AdjListNode { int dest; struct AdjListNode* next; }; // A structure to represent an adjacency list struct AdjList { struct AdjListNode *head; // pointer to head node of list }; // A structure to represent a graph. A graph is an array of adjacency lists. // Size of array will be V (number of vertices in graph) struct Graph { int V; struct AdjList* array; }; // A utility function to create a new adjacency list node struct AdjListNode* newAdjListNode(int dest) { struct AdjListNode* newNode = (struct AdjListNode*) malloc(sizeof(struct AdjListNode)); newNode->dest = dest; newNode->next = NULL; return newNode; } // A utility function that creates a graph of V vertices struct Graph* createGraph(int V) { struct Graph* graph = (struct Graph*) malloc(sizeof(struct Graph)); graph->V = V; // Create an array of adjacency lists. Size of array will be V graph->array = (struct AdjList*) malloc(V * sizeof(struct AdjList)); // Initialize each adjacency list as empty by making head as NULL int i; for (i = 0; i < V; ++i) graph->array[i].head = NULL; return graph; } // Adds an edge to an undirected graph void addEdge(struct Graph* graph, int src, int dest) { // Add an edge from src to dest. A new node is added to the adjacency // list of src. The node is added at the begining struct AdjListNode* newNode = newAdjListNode(dest); newNode->next = graph->array[src].head; graph->array[src].head = newNode; // Since graph is undirected, add an edge from dest to src also newNode = newAdjListNode(src); newNode->next = graph->array[dest].head; graph->array[dest].head = newNode; } // A utility function to print the adjacency list representation of graph void printGraph(struct Graph* graph) { int v; for (v = 0; v < graph->V; ++v) { struct AdjListNode* pCrawl = graph->array[v].head; printf(" Adjacency list of vertex %d head ", v); while (pCrawl) { printf("-> %d", pCrawl->dest); pCrawl = pCrawl->next; } printf(" "); } } // Driver program to test above functions int main() { // create the graph given in above figure int V = 5; struct Graph* graph = createGraph(V); addEdge(graph, 0, 1); addEdge(graph, 0, 4); addEdge(graph, 1, 2); addEdge(graph, 1, 3); addEdge(graph, 1, 4); addEdge(graph, 2, 3); addEdge(graph, 3, 4); // print the adjacency list representation of the above graph printGraph(graph); return 0; }
Output:
Adjacency list of vertex 0 head -> 4-> 1 Adjacency list of vertex 1 head -> 4-> 3-> 2-> 0 Adjacency list of vertex 2 head -> 3-> 1 Adjacency list of vertex 3 head -> 4-> 2-> 1 Adjacency list of vertex 4 head -> 3-> 1-> 0
Pros
Cons
Breadth-First Search (BFS) traverses a graph level by level, visiting all neighbours of a vertex before moving to the next level. Unlike a tree, a graph may contain cycles; to avoid processing a node more than once we use a visited array (or set).
Example: starting BFS from vertex 2 in the following graph gives the traversal 2, 0, 3, 1.
Typical behaviour and notes:
C++ implementation using adjacency lists and STL containers:
// Program to print BFS traversal from a given source vertex. BFS(int s) // traverses vertices reachable from s. #include<iostream> #include <list> using namespace std; // This class represents a directed graph using adjacency list representation class Graph { int V; // No. of vertices list<int> *adj; // Pointer to an array containing adjacency lists public: Graph(int V); // Constructor void addEdge(int v, int w); // function to add an edge to graph void BFS(int s); // prints BFS traversal from a given source s }; Graph::Graph(int V) { this->V = V; adj = new list<int>[V]; } void Graph::addEdge(int v, int w) { adj[v].push_back(w); // Add w to v's list. } void Graph::BFS(int s) { // Mark all the vertices as not visited bool *visited = new bool[V]; for(int i = 0; i < V; i++) visited[i] = false; // Create a queue for BFS list<int> queue; // Mark the current node as visited and enqueue it visited[s] = true; queue.push_back(s); // 'i' will be used to get all adjacent vertices of a vertex list<int>::iterator i; while(!queue.empty()) { // Dequeue a vertex from queue and print it s = queue.front(); cout << s << " "; queue.pop_front(); // Get all adjacent vertices of the dequeued vertex s // If an adjacent has not been visited, then mark it visited // and enqueue it for(i = adj[s].begin(); i != adj[s].end(); ++i) { if(!visited[*i]) { visited[*i] = true; queue.push_back(*i); } } } } // Driver program to test methods of graph class int main() { // Create a graph given in the above diagram Graph g(4); g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 2); g.addEdge(2, 0); g.addEdge(2, 3); g.addEdge(3, 3); cout << "Following is Breadth First Traversal " << "(starting from vertex 2) "; g.BFS(2); return 0; }
Output:
Following is Breadth First Traversal (starting from vertex 2) 2 0 3 1
Time complexity: O(V + E), where V is the number of vertices and E is the number of edges. Each vertex and each edge is processed at most once.
Depth-First Search (DFS) explores as far as possible along each branch before backtracking. As with BFS, graphs may contain cycles so use a visited array to avoid infinite loops.
Example: starting DFS from vertex 2 in the following graph may yield traversal 2, 0, 1, 3.
Recursive C++ implementation using adjacency lists:
// C++ program to print DFS traversal from a given vertex in a given graph #include<iostream> #include<list> using namespace std; // Graph class represents a directed graph using adjacency list representation class Graph { int V; // No. of vertices list<int> *adj; // Pointer to an array containing adjacency lists void DFSUtil(int v, bool visited[]); // A function used by DFS public: Graph(int V); // Constructor void addEdge(int v, int w); // function to add an edge to graph void DFS(int v); // DFS traversal of the vertices reachable from v }; Graph::Graph(int V) { this->V = V; adj = new list<int>[V]; } void Graph::addEdge(int v, int w) { adj[v].push_back(w); // Add w to v's list. } void Graph::DFSUtil(int v, bool visited[]) { // Mark the current node as visited and print it visited[v] = true; cout << v << " "; // Recur for all the vertices adjacent to this vertex list<int>::iterator i; for (i = adj[v].begin(); i != adj[v].end(); ++i) if (!visited[*i]) DFSUtil(*i, visited); } // DFS traversal of the vertices reachable from v. // It uses recursive DFSUtil() void Graph::DFS(int v) { // Mark all the vertices as not visited bool *visited = new bool[V]; for (int i = 0; i < V; i++) visited[i] = false; // Call the recursive helper function to print DFS traversal DFSUtil(v, visited); } int main() { // Create a graph given in the above diagram Graph g(4); g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 2); g.addEdge(2, 0); g.addEdge(2, 3); g.addEdge(3, 3); cout << "Following is Depth First Traversal (starting from vertex 2) "; g.DFS(2); return 0; }
Output:
Following is Depth First Traversal (starting from vertex 2) 2 0 1 3
To traverse a complete graph (including disconnected components), call DFS helper for every vertex that remains unvisited. Example implementation (complete traversal) is shown below:
// C++ program to print DFS traversal for a given given graph #include<iostream> #include <list> using namespace std; class Graph { int V; // No. of vertices list<int> *adj; // Pointer to an array containing adjacency lists void DFSUtil(int v, bool visited[]); // A function used by DFS public: Graph(int V); // Constructor void addEdge(int v, int w); // function to add an edge to graph void DFS(); // prints DFS traversal of the complete graph }; Graph::Graph(int V) { this->V = V; adj = new list<int>[V]; } void Graph::addEdge(int v, int w) { adj[v].push_back(w); // Add w to v's list. } void Graph::DFSUtil(int v, bool visited[]) { // Mark the current node as visited and print it visited[v] = true; cout << v << " "; // Recur for all the vertices adjacent to this vertex list<int>::iterator i; for(i = adj[v].begin(); i != adj[v].end(); ++i) if(!visited[*i]) DFSUtil(*i, visited); } // The function to do DFS traversal. It uses recursive DFSUtil() void Graph::DFS() { // Mark all the vertices as not visited bool *visited = new bool[V]; for (int i = 0; i < V; i++) visited[i] = false; // Call the recursive helper function to print DFS traversal // starting from all vertices one by one for (int i = 0; i < V; i++) if (visited[i] == false) DFSUtil(i, visited); } int main() { // Create a graph given in the above diagram Graph g(4); g.addEdge(0, 1); g.addEdge(0, 2); g.addEdge(1, 2); g.addEdge(2, 0); g.addEdge(2, 3); g.addEdge(3, 3); cout << "Following is Depth First Traversal "; g.DFS(); return 0; }
Output:
Following is Depth First Traversal 0 1 2 3
Time complexity: O(V + E). Each vertex and each edge is visited at most once in the traversal.
Note on iterative DFS: DFS can be implemented iteratively using an explicit stack instead of recursion; this is useful where recursion depth is a concern.
DFS is a versatile technique used as a building block for many graph algorithms. Common applications include:
BFS is also fundamental and has many applications:
Graphs model relational data and have multiple representations. Choose adjacency lists for sparse graphs and adjacency matrices for dense graphs or if you need O(1) edge queries. BFS finds shortest paths in unweighted graphs and explores level by level; DFS explores deep paths and is widely used for cycle detection, topological sort and strongly connected components. Both BFS and DFS run in linear time in the size of the graph (O(V + E)) when adjacency lists are used.
| 1. What is a graph in mathematics? | ![]() |
| 2. What are the common types of graphs used in mathematics? | ![]() |
| 3. What are the applications of graphs in real-life scenarios? | ![]() |
| 4. How are vertices and edges represented in a graph? | ![]() |
| 5. What is the difference between an undirected graph and a directed graph? | ![]() |
![]() | Explore Courses for Computer Science Engineering (CSE) exam |
![]() | Get EduRev Notes directly in your Google search |