Graph representations using set and hash
Last Updated :
08 Mar, 2023
We have introduced Graph implementation using array of vectors in Graph implementation using STL for competitive programming | Set 1. In this post, a different implementation is used which can be used to implement graphs using sets. The implementation is for adjacency list representation of graph.
A set is different from a vector in two ways: it stores elements in a sorted way, and duplicate elements are not allowed. Therefore, this approach cannot be used for graphs containing parallel edges. Since sets are internally implemented as binary search trees, an edge between two vertices can be searched in O(logV) time, where V is the number of vertices in the graph. Sets in python are unordered and not indexed. Hence, for python we will be using dictionary which will have source vertex as key and its adjacency list will be stored in a set format as value for that key.
Following is an example of an undirected and unweighted graph with 5 vertices.
Below is adjacency list representation of this graph using array of sets.
Below is the code for adjacency list representation of an undirected graph using sets:
C++
#include <bits/stdc++.h>
using namespace std;
struct Graph {
int V;
set< int >* adjList;
};
Graph* createGraph( int V)
{
Graph* graph = new Graph;
graph->V = V;
graph->adjList = new set< int >[V];
return graph;
}
void addEdge(Graph* graph, int src, int dest)
{
graph->adjList[src].insert(dest);
graph->adjList[dest].insert(src);
}
void printGraph(Graph* graph)
{
for ( int i = 0; i < graph->V; ++i) {
set< int > lst = graph->adjList[i];
cout << endl << "Adjacency list of vertex "
<< i << endl;
for ( auto itr = lst.begin(); itr != lst.end(); ++itr)
cout << *itr << " " ;
cout << endl;
}
}
void searchEdge(Graph* graph, int src, int dest)
{
auto itr = graph->adjList[src].find(dest);
if (itr == graph->adjList[src].end())
cout << endl << "Edge from " << src
<< " to " << dest << " not found."
<< endl;
else
cout << endl << "Edge from " << src
<< " to " << dest << " found."
<< endl;
}
int main()
{
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);
printGraph(graph);
searchEdge(graph, 2, 1);
searchEdge(graph, 0, 3);
return 0;
}
|
Java
import java.util.*;
class Graph {
HashMap<Integer, TreeSet<Integer> > graph;
static int v;
public Graph()
{
graph = new HashMap<>();
for ( int i = 0 ; i < v; i++) {
graph.put(i, new TreeSet<>());
}
}
public void addEdge( int src, int dest)
{
graph.get(src).add(dest);
graph.get(dest).add(src);
}
public void printGraph()
{
for ( int i = 0 ; i < v; i++) {
System.out.println( "Adjacency list of vertex "
+ i);
Iterator set = graph.get(i).iterator();
while (set.hasNext())
System.out.print(set.next() + " " );
System.out.println();
System.out.println();
}
}
public void searchEdge( int src, int dest)
{
Iterator set = graph.get(src).iterator();
if (graph.get(src).contains(dest))
System.out.println( "Edge from " + src + " to "
+ dest + " found" );
else
System.out.println( "Edge from " + src + " to "
+ dest + " not found" );
System.out.println();
}
public static void main(String[] args)
{
v = 5 ;
Graph graph = new Graph();
graph.addEdge( 0 , 1 );
graph.addEdge( 0 , 4 );
graph.addEdge( 1 , 2 );
graph.addEdge( 1 , 3 );
graph.addEdge( 1 , 4 );
graph.addEdge( 2 , 3 );
graph.addEdge( 3 , 4 );
graph.printGraph();
graph.searchEdge( 2 , 1 );
graph.searchEdge( 0 , 3 );
}
}
|
Python3
from collections import defaultdict
class graph( object ):
def __init__( self , v):
self .v = v
self .graph = defaultdict( set )
def addEdge( self , source, destination):
self .graph.add(destination)
self .graph[destination].add(source)
def print ( self ):
for i, adjlist in sorted ( self .graph.items()):
print ( "Adjacency list of vertex " , i)
for j in sorted (adjlist, reverse = True ):
print (j, end = " " )
print ( '\n' )
def searchEdge( self ,source,destination):
if source in self .graph:
if destination in self .graph:
if destination in self .graph:
if source in self .graph[destination]:
print ( "Edge from {0} to {1} found.\n" . format (source, destination))
return
else :
print ( "Edge from {0} to {1} not found.\n" . format (source, destination))
return
else :
print ( "Edge from {0} to {1} not found.\n" . format (source, destination))
return
else :
print ( "Destination vertex {} is not present in graph.\n" . format (destination))
return
else :
print ( "Source vertex {} is not present in graph.\n" . format (source))
if __name__ = = "__main__" :
g = graph( 5 )
g.addEdge( 0 , 1 )
g.addEdge( 0 , 4 )
g.addEdge( 1 , 2 )
g.addEdge( 1 , 3 )
g.addEdge( 1 , 4 )
g.addEdge( 2 , 3 )
g.addEdge( 3 , 4 )
g. print ()
g.searchEdge( 2 , 1 )
g.searchEdge( 0 , 3 )
|
C#
using System;
using System.Collections.Generic;
class Graph {
Dictionary< int , HashSet< int >> graph;
static int v;
public Graph()
{
graph = new Dictionary< int , HashSet< int > >();
for ( int i = 0; i < v; i++) {
graph.Add(i, new HashSet< int >());
}
}
public void addEdge( int src, int dest)
{
graph[src].Add(dest);
graph[dest].Add(src);
}
public void printGraph()
{
for ( int i = 0; i < v; i++) {
Console.WriteLine( "Adjacency list of vertex "
+ i);
foreach ( int set_ in graph[i])
Console.Write(set_ + " " );
Console.WriteLine();
Console.WriteLine();
}
}
public void searchEdge( int src, int dest)
{
if (graph[src].Contains(dest))
Console.WriteLine( "Edge from " + src + " to "
+ dest + " found" );
else
Console.WriteLine( "Edge from " + src + " to "
+ dest + " not found" );
Console.WriteLine();
}
public static void Main(String[] args)
{
v = 5;
Graph graph = new Graph();
graph.addEdge(0, 1);
graph.addEdge(0, 4);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(2, 3);
graph.addEdge(3, 4);
graph.printGraph();
graph.searchEdge(2, 1);
graph.searchEdge(0, 3);
}
}
|
Javascript
<script>
class Graph {
constructor()
{
this .V = 0;
this .adjList = new Set();
}
};
function createGraph(V)
{
var graph = new Graph();
graph.V = V;
graph.adjList = Array.from(Array(V), ()=> new Set());
return graph;
}
function addEdge(graph, src, dest)
{
graph.adjList[src].add(dest);
graph.adjList[dest].add(src);
}
function printGraph(graph)
{
for ( var i = 0; i < graph.V; ++i) {
var lst = graph.adjList[i];
document.write( "<br>" + "Adjacency list of vertex "
+ i + "<br>" );
for ( var item of [...lst].reverse())
document.write( item + " " );
document.write( "<br>" );
}
}
function searchEdge(graph, src, dest)
{
if (!graph.adjList[src].has(dest))
document.write( "Edge from " + src
+ " to " + dest + " not found.<br>" );
else
document.write( "<br> Edge from " + src
+ " to " + dest + " found." + "<br><br>" );
}
var V = 5;
var 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);
printGraph(graph);
searchEdge(graph, 2, 1);
searchEdge(graph, 0, 3);
</script>
|
Output
Adjacency list of vertex 0
1 4
Adjacency list of vertex 1
0 2 3 4
Adjacency list of vertex 2
1 3
Adjacency list of vertex 3
1 2 4
Adjacency list of vertex 4
0 1 3
Edge from 2 to 1 found.
Edge from 0 to 3 not found.
Pros: Queries like whether there is an edge from vertex u to vertex v can be done in O(log V).
Cons:
- Adding an edge takes O(log V), as opposed to O(1) in vector implementation.
- Graphs containing parallel edge(s) cannot be implemented through this method.
Space Complexity: O(V+E), where V is the number of vertices and E is the number of edges in the graph. This is because the code uses an adjacency list to store the graph, which takes linear space.
Further Optimization of Edge Search Operation using unordered_set (or hashing): The edge search operation can be further optimized to O(1) using unordered_set which uses hashing internally.
Implementation:
C++
#include <bits/stdc++.h>
using namespace std;
struct Graph {
int V;
unordered_set< int >* adjList;
};
Graph* createGraph( int V)
{
Graph* graph = new Graph;
graph->V = V;
graph->adjList = new unordered_set< int >[V];
return graph;
}
void addEdge(Graph* graph, int src, int dest)
{
graph->adjList[src].insert(dest);
graph->adjList[dest].insert(src);
}
void printGraph(Graph* graph)
{
for ( int i = 0; i < graph->V; ++i) {
unordered_set< int > lst = graph->adjList[i];
cout << endl << "Adjacency list of vertex "
<< i << endl;
for ( auto itr = lst.begin(); itr != lst.end(); ++itr)
cout << *itr << " " ;
cout << endl;
}
}
void searchEdge(Graph* graph, int src, int dest)
{
auto itr = graph->adjList[src].find(dest);
if (itr == graph->adjList[src].end())
cout << endl << "Edge from " << src
<< " to " << dest << " not found."
<< endl;
else
cout << endl << "Edge from " << src
<< " to " << dest << " found."
<< endl;
}
int main()
{
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);
printGraph(graph);
searchEdge(graph, 2, 1);
searchEdge(graph, 0, 3);
return 0;
}
|
Java
import java.util.HashSet;
import java.util.Set;
class Graph {
int V;
Set<Integer>[] adjList;
public Graph( int V)
{
this .V = V;
adjList = new HashSet[V];
for ( int i = 0 ; i < V; i++) {
adjList[i] = new HashSet<Integer>();
}
}
void addEdge( int src, int dest)
{
adjList[src].add(dest);
adjList[dest].add(src);
}
void printGraph()
{
for ( int i = 0 ; i < V; i++) {
Set<Integer> lst = adjList[i];
System.out.println( "Adjacency list of vertex "
+ i);
for (Integer itr : lst) {
System.out.print(itr + " " );
}
System.out.println();
}
}
void searchEdge( int src, int dest)
{
if (!adjList[src].contains(dest)) {
System.out.println( "Edge from " + src + " to "
+ dest + " not found." );
}
else {
System.out.println( "Edge from " + src + " to "
+ dest + " found." );
}
}
}
public class Main {
public static void main(String[] args)
{
int V = 5 ;
Graph graph = new Graph(V);
graph.addEdge( 0 , 1 );
graph.addEdge( 0 , 4 );
graph.addEdge( 1 , 2 );
graph.addEdge( 1 , 3 );
graph.addEdge( 1 , 4 );
graph.addEdge( 2 , 3 );
graph.addEdge( 3 , 4 );
graph.printGraph();
graph.searchEdge( 2 , 1 );
graph.searchEdge( 0 , 3 );
}
}
|
Python3
import collections
class Graph:
def __init__( self , V):
self .V = V
self .adjList = [ set () for _ in range (V)]
def add_edge( self , src, dest):
self .adjList[src].add(dest)
self .adjList[dest].add(src)
def print_graph( self ):
for i in range ( self .V):
print ( "Adjacency list of vertex {}" . format (i))
for vertex in self .adjList[i]:
print (vertex, end = ' ' )
print ()
def search_edge( self , src, dest):
if dest in self .adjList[src]:
print ( "Edge from {} to {} found." . format (src, dest))
else :
print ( "Edge from {} to {} not found." . format (src, dest))
if __name__ = = "__main__" :
V = 5
graph = Graph(V)
graph.add_edge( 0 , 1 )
graph.add_edge( 0 , 4 )
graph.add_edge( 1 , 2 )
graph.add_edge( 1 , 3 )
graph.add_edge( 1 , 4 )
graph.add_edge( 2 , 3 )
graph.add_edge( 3 , 4 )
graph.print_graph()
graph.search_edge( 2 , 1 )
graph.search_edge( 0 , 3 )
|
C#
using System;
using System.Collections.Generic;
class Graph {
int V;
HashSet< int >[] adjList;
public Graph( int V)
{
this .V = V;
adjList = new HashSet< int >[ V ];
for ( int i = 0; i < V; i++) {
adjList[i] = new HashSet< int >();
}
}
void addEdge( int src, int dest)
{
adjList[src].Add(dest);
adjList[dest].Add(src);
}
void printGraph()
{
for ( int i = 0; i < V; i++) {
HashSet< int > lst = adjList[i];
Console.WriteLine( "Adjacency list of vertex "
+ i);
foreach ( int itr in lst)
{
Console.Write(itr + " " );
}
Console.WriteLine();
}
}
void searchEdge( int src, int dest)
{
if (!adjList[src].Contains(dest)) {
Console.WriteLine( "Edge from " + src + " to "
+ dest + " not found." );
}
else {
Console.WriteLine( "Edge from " + src + " to "
+ dest + " found." );
}
}
public static void Main( string [] args)
{
int V = 5;
Graph graph = new Graph(V);
graph.addEdge(0, 1);
graph.addEdge(0, 4);
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(1, 4);
graph.addEdge(2, 3);
graph.addEdge(3, 4);
graph.printGraph();
graph.searchEdge(2, 1);
graph.searchEdge(0, 3);
}
}
|
Javascript
<script>
class Graph {
constructor(V) {
this .V = V;
this .adjList = new Array(V).fill().map(() => new Set());
}
}
function addEdge(graph, src, dest) {
graph.adjList[src].add(dest);
graph.adjList[dest].add(src);
}
function printGraph(graph) {
for (let i = 0; i < graph.V; ++i) {
const lst = graph.adjList[i];
console.log(`\nAdjacency list of vertex ${i}\n`);
for (const element of lst) {
console.log(element);
}
}
}
function searchEdge(graph, src, dest) {
if (graph.adjList[src].has(dest)) {
console.log(`\nEdge from ${src} to ${dest} found.\n`);
} else {
console.log(`\nEdge from ${src} to ${dest} not found.\n`);
}
}
const V = 5;
const graph = new Graph(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);
printGraph(graph);
searchEdge(graph, 2, 1);
searchEdge(graph, 0, 3);
</script>
|
Output
Adjacency list of vertex 0
4 1
Adjacency list of vertex 1
4 3 2 0
Adjacency list of vertex 2
3 1
Adjacency list of vertex 3
4 2 1
Adjacency list of vertex 4
3 1 0
Edge from 2 to 1 found.
Edge from 0 to 3 not found.
Time Complexity: The time complexity of creating a graph using adjacency list is O(V + E), where V is the number of vertices and E is the number of edges in the graph.
Space Complexity: The space complexity of creating a graph using adjacency list is O(V + E), where V is the number of vertices and E is the number of edges in the graph.
Pros:
- Queries like whether there is an edge from vertex u to vertex v can be done in O(1).
- Adding an edge takes O(1).
Cons:
- Graphs containing parallel edge(s) cannot be implemented through this method.
- Edges are stored in any order.
Note : adjacency matrix representation is the most optimized for edge search, but space requirements of adjacency matrix are comparatively high for big sparse graphs. Moreover adjacency matrix has other disadvantages as well like BFS and DFS become costly as we can’t quickly get all adjacent of a node.
Please Login to comment...